diff --git a/.circleci/config.yml b/.circleci/config.yml index 2b9fba2ed..827b9e29b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ jobs: lint-testunit: <<: *defaults docker: - - image: circleci/node:8 + - image: circleci/node:10 environment: CODECOV_TOKEN: caa771ab-3d45-4756-8e2a-e1f25996fef6 @@ -59,13 +59,13 @@ jobs: key: node-v1-mac-{{ checksum "yarn.lock" }} - run: - name: Install Node 8 + 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 8 + nvm install 10 - run: name: Install appleSimUtils @@ -120,13 +120,13 @@ jobs: key: node-v1-mac-{{ checksum "yarn.lock" }} - run: - name: Install Node 8 + 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 8 + nvm install 10 - run: name: Install appleSimUtils @@ -176,14 +176,14 @@ jobs: - checkout - run: - name: Install Node 8 + 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 8 - echo 'export PATH="/home/circleci/.nvm/versions/node/v8.16.0/bin:$PATH"' >> ~/.bash_profile + nvm install 10 + echo 'export PATH="/home/circleci/.nvm/versions/node/v10.20.1/bin:$PATH"' >> ~/.bash_profile source ~/.bash_profile - restore_cache: @@ -206,6 +206,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 +235,7 @@ jobs: name: Build Android App command: | if [[ $KEYSTORE ]]; then - # TODO: enable app bundle again - ./gradlew assembleRelease + ./gradlew bundleRelease else ./gradlew assembleDebug fi @@ -292,13 +292,13 @@ jobs: key: node-v1-mac-{{ checksum "yarn.lock" }} - run: - name: Install Node 8 + 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 8 + nvm install 10 - run: name: Install NPM modules diff --git a/.eslintrc.js b/.eslintrc.js index b7c949197..bd13e5051 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -131,7 +131,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/__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 4338ff0f3..3169fb2e3 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -3180,455 +3180,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,239 +3216,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 - - - - - - -  - - - - - 00:00 - - - - - This is a description - - - - - - - - - - - - - - - - - - - First message - - - - - - - - - - - - - - - - - -  - - - - - 00:00 - - - - - This is a description - - - - - - - - - - - - - - - - -  - - - - - 00:00 - - - - - - - - - - - - - - - - -  - - - - - 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 - - - - - - - - `; diff --git a/android/app/build.gradle b/android/app/build.gradle index 44b16c639..cacb3f5db 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -19,7 +19,9 @@ import com.android.build.OutputFile * // the name of the generated asset file containing your JS bundle * bundleAssetName: "index.android.bundle", * - * // the entry file for bundle generation + * // the entry file for bundle generation. If none specified and + * // "index.android.js" exists, it will be used. Otherwise "index.js" is + * // default. Can be overridden with ENTRY_FILE environment variable. * entryFile: "index.android.js", * * // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format @@ -80,7 +82,6 @@ import com.android.build.OutputFile */ project.ext.react = [ - entryFile: "index.js", bundleAssetName: "app.bundle", iconFontNames: [ 'custom.ttf' ], enableHermes: true, // clean and rebuild if changing @@ -141,6 +142,7 @@ android { versionName "4.7.0" vectorDrawables.useSupportLibrary = true manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] + missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below! } signingConfigs { @@ -168,6 +170,14 @@ android { signingConfig signingConfigs.release } } + + packagingOptions { + pickFirst '**/armeabi-v7a/libc++_shared.so' + pickFirst '**/x86/libc++_shared.so' + pickFirst '**/arm64-v8a/libc++_shared.so' + pickFirst '**/x86_64/libc++_shared.so' + } + // applicationVariants are e.g. debug, release applicationVariants.all { variant -> 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/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 79b4ad22e..c242cc02f 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,7 @@ 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.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 2357aa650..a7df71591 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -19,7 +19,16 @@ # 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 + +# Version of flipper SDK to use with React Native +FLIPPER_VERSION=0.33.1 + +# App properties VERSIONCODE=999999999 -BugsnagAPIKey="" +BugsnagAPIKey="" \ 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/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/lib/rocketchat.js b/app/lib/rocketchat.js index 80fd29727..4ab0539df 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -378,44 +378,32 @@ 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; - } + 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, 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/share.js b/app/share.js index 5ab50599a..8be11d629 100644 --- a/app/share.js +++ b/app/share.js @@ -15,7 +15,7 @@ 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'; @@ -108,7 +108,7 @@ class Root extends React.Component { return ( 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/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/LoginView.js b/app/views/LoginView.js index 3d81b0f58..e209e058c 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')); 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/RoomView/List.js b/app/views/RoomView/List.js index 8860b8de7..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`); @@ -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/RoomsListView/index.js b/app/views/RoomsListView/index.js index bb95377ef..42ff5b3aa 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -235,7 +235,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) { diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js index 55d00d71d..3deb9685f 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/index.js b/app/views/SidebarView/index.js index cad318486..25e81c4b5 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(); 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..e4572063e 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' @@ -76,10 +132,16 @@ target 'ShareRocketChatRN' do use_native_modules! 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 6b5c50ef3..91820319b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,76 +1,124 @@ 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 + - EXFileSystem (8.1.0): + - UMCore + - UMFileSystemInterface + - EXHaptics (8.1.0): + - UMCore + - EXImageLoader (1.0.1): + - React-Core + - UMCore + - UMImageLoaderInterface - EXKeepAwake (8.1.0): - UMCore - - EXPermissions (6.0.0): + - 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 @@ -81,35 +129,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): @@ -121,320 +170,370 @@ 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-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): + - RNDateTimePicker (2.3.2): - React - - RNDeviceInfo (2.3.2): + - RNDeviceInfo (5.5.7): - React - - RNFastImage (7.0.2): + - 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): + - RNImageCropPicker (0.30.0): - React-Core - React-RCTImage - - RNImageCropPicker/QBImagePickerController (= 0.28.0) + - RNImageCropPicker/QBImagePickerController (= 0.30.0) - RSKImageCropper - - RNImageCropPicker/QBImagePickerController (0.28.0): + - RNImageCropPicker/QBImagePickerController (0.30.0): - 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`) - 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`) @@ -455,8 +554,6 @@ 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`)" @@ -470,9 +567,11 @@ 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`) @@ -489,6 +588,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`) @@ -506,6 +606,8 @@ DEPENDENCIES: SPEC REPOS: trunk: - boost-for-react-native + - CocoaAsyncSocket + - CocoaLibEvent - Crashlytics - Fabric - Firebase @@ -514,47 +616,47 @@ 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: !ruby/object:Pathname - path: "../node_modules/expo-keep-awake/ios" + :path: "../node_modules/expo-keep-awake/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,10 +697,6 @@ 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: @@ -629,6 +727,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: @@ -661,148 +763,149 @@ 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 + EXAV: 2edd9cd30fe98d04b55325303c7ff01db02d1d0f + EXConstants: 5304709b1bea70a4828f48ba4c7fc3ec3b2d9b17 + EXFileSystem: cf4232ba7c62dc49b78c2d36005f97b6fddf0b01 + EXHaptics: 013b5065946d4dd7b46ea547b58072d45a206dbd + EXImageLoader: 5ad6896fa1ef2ee814b551873cbf7a7baccc694a EXKeepAwake: d045bc2cf1ad5a04f0323cc7c894b95b414042e0 - EXPermissions: 99e52dc3e5f8e55153f1958004f6df2a30a1f2f5 - EXWebBrowser: def838b95aa9d396f9ce71ace4e614ee16e7ee30 + 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-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 - RNImageCropPicker: cf129d17e042ce3e96fb9ada967c28f21f977c82 - RNLocalize: 07eb7a91d10021cdf59d80061ebf3adb8a5b5688 - RNReanimated: b2ab0b693dddd2339bd2f300e770f6302d2e960c + RNBootSplash: 7cb9b4fe7e94177edc0d11010f7631d79db2f5e9 + RNDateTimePicker: 4bd49e09f91ca73d69119a9e1173b0d43b82f5e5 + RNDeviceInfo: e2102056bde3ad5d137fd029d8d431510a00486a + RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9 + RNFirebase: 37daa9a346d070f9f6ee1f3b4aaf4c8e3b1d5d1c + RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38 + RNImageCropPicker: a606d65f71c6c05caa3c850c16fb1ba2a4718608 + 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: 929bf93b227155c1405c8133dd38934cb852bb55 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.
+ _No need to muck around with sockets or streams. This class handles everything for you._ + +- Full delegate support
+ _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.
+ _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.
+ _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.
+ _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
+ _Secure your socket with ease using just a single method call. Available for both client and server sockets._ + +- Fully GCD based and Thread-Safe
+ _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.
+ _No need to muck around with low-level sockets. This class handles everything for you._ + +- Full delegate support.
+ _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.
+ _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.
+ _Automatically send/recv using IPv4 and/or IPv6. No more worrying about multiple sockets._ + +- Fully GCD based and Thread-Safe
+ _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)**.
_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 +#import +#import +#import +#import + +#include // 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)aDelegate delegateQueue:(nullable dispatch_queue_t)dq; +- (instancetype)initWithDelegate:(nullable id)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)aDelegate delegateQueue:(nullable dispatch_queue_t)dq error:(NSError**)error; + ++ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id)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 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 __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr; +- (void)setDelegate:(nullable id)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)delegate; +- (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue; +- (void)synchronouslySetDelegate:(nullable id)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 *)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 +@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 +#endif + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#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 *)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 *)settings +{ + if((self = [super init])) + { + tlsSettings = [settings copy]; + } + return self; +} + + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation GCDAsyncSocket +{ + uint32_t flags; + uint16_t config; + + __weak id 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)aDelegate delegateQueue:(dispatch_queue_t)dq +{ + return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL]; +} + +- (instancetype)initWithDelegate:(id)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)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)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)newDelegate +{ + [self setDelegate:newDelegate synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id)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 *)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)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id)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 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 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 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 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 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 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 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 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 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 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 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 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 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 +#import +#import +#import + +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 +@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)aDelegate delegateQueue:(nullable dispatch_queue_t)dq; +- (instancetype)initWithDelegate:(nullable id)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq NS_DESIGNATED_INITIALIZER; + +#pragma mark Configuration + +- (nullable id)delegate; +- (void)setDelegate:(nullable id)delegate; +- (void)synchronouslySetDelegate:(nullable id)delegate; + +- (nullable dispatch_queue_t)delegateQueue; +- (void)setDelegateQueue:(nullable dispatch_queue_t)delegateQueue; +- (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue; + +- (void)getDelegate:(id __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr; +- (void)setDelegate:(nullable id)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue; +- (void)synchronouslySetDelegate:(nullable id)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 + #import +#endif + +#import +#import +#import +#import +#import +#import +#import + + +#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)aDelegate delegateQueue:(dispatch_queue_t)dq +{ + LogTrace(); + + return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL]; +} + +- (instancetype)initWithDelegate:(id)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)delegate +{ + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + { + return delegate; + } + else + { + __block id result = nil; + + 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)newDelegate +{ + [self setDelegate:newDelegate synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id)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 *)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)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue +{ + [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:NO]; +} + +- (void)synchronouslySetDelegate:(id)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 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 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 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 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 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 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 + * 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 header is deprecated in Libevent 2.0 and later; please + use instead. Depending on what functionality you + need, you may also want to include more of the other + headers. + */ + +#include +#include +#include +#include + +#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 + * 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 header is deprecated in Libevent 2.0 and later; please + use 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 +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif +#ifdef EVENT__HAVE_STDINT_H +#include +#endif +#include + +/* For int types. */ +#include + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#undef WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_UIO_H +#include +#endif +#include + +/** + 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 + +/** @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 + * 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: + +

+
Socket-based bufferevents
+
A bufferevent that reads and writes data onto a network + socket. Created with bufferevent_socket_new().
+ +
Paired bufferevents
+
A pair of bufferevents that send and receive data to one + another without touching the network. Created with + bufferevent_pair_new().
+ +
Filtering bufferevents
+
A bufferevent that transforms data, and sends or receives it + over another underlying bufferevent. Created with + bufferevent_filter_new().
+ +
SSL-backed bufferevents
+
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().
+
+ */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** @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 + * 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 +#include +#include +#include + +#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 + * 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 +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include +/* For struct event */ +#include + +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 + * 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 + * + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For integer types. */ +#include + +/** 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 + * 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 +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + 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 + * 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 +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/* + * 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 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 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 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 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 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 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 header file. */ +#define EVENT__HAVE_MACH_MACH_TIME_H 1 + +/* Define to 1 if you have the 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 header file. */ +#define EVENT__HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_NETINET_IN6_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the 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 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 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 */ +#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 header file. */ +#define EVENT__HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STRINGS_H 1 + +/* Define to 1 if you have the 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 header file. */ +/* #undef EVENT__HAVE_SYS_DEVPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EVENTFD_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_EVENT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_QUEUE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_SENDFILE_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SYSCTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_TIMERFD_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_WAIT_H 1 + +/* Define if TAILQ_FOREACH is defined in */ +#define EVENT__HAVE_TAILQFOREACH 1 + +/* Define if timeradd is defined in */ +#define EVENT__HAVE_TIMERADD 1 + +/* Define if timerclear is defined in */ +#define EVENT__HAVE_TIMERCLEAR 1 + +/* Define if timercmp is defined in */ +#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 */ +#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 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 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 and . */ +#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 does not define. */ +/* #undef EVENT__pid_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef EVENT__size_t */ + +/* Define to unsigned int if you dont have it */ +/* #undef EVENT__socklen_t */ + +/* Define to `int' if 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 + * 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 + 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 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 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 + 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 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +#include + +/* For int types. */ +#include + +/** + * 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: +
+     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);
+     }
+   
+ + @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: +
+      struct event *ev = event_new(base, sock, events, callback, %event_self_cbarg());
+  
+ + 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 + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + 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 + * 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 +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/* For evkeyvalq */ +#include + +#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 */ +#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 */ +#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 + * 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 +#include + +#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: + *
    + *
  • Nonconformant URIs are allowed to contain otherwise unreasonable + * characters in their path, query, and fragment components. + *
+ */ +#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 + * 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 +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + * 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 + * 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 +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + * 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 + * 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 */ +/* 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 + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +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 + * 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 + * 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 + * 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 + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +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 + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + @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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif +#ifdef EVENT__HAVE_STDINT_H +#include +#elif defined(EVENT__HAVE_INTTYPES_H) +#include +#endif +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_STDDEF_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#ifdef EVENT__HAVE_NETDB_H +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#include +#endif + +#ifdef _WIN32 +#include +#ifdef EVENT__HAVE_GETADDRINFO +/* for EAI_* definitions. */ +#include +#endif +#else +#ifdef EVENT__HAVE_ERRNO_H +#include +#endif +#include +#endif + +#include + +/* 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: + * + *
+ *
ev_uint64_t, ev_uint32_t, ev_uint16_t, ev_uint8_t
+ *
unsigned integer types of exactly 64, 32, 16, and 8 bits + * respectively.
+ *
ev_int64_t, ev_int32_t, ev_int16_t, ev_int8_t
+ *
signed integer types of exactly 64, 32, 16, and 8 bits + * respectively.
+ *
ev_uintptr_t, ev_intptr_t
+ *
unsigned/signed integers large enough + * to hold a pointer without loss of bits.
+ *
ev_ssize_t
+ *
A signed type of the same size as size_t
+ *
ev_off_t
+ *
A signed type typically used to represent offsets within a + * (potentially large) file
+ * + * @{ + */ +#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 + +#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 + * 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 header is deprecated in Libevent 2.0 and later; please + use instead. Depending on what functionality you + need, you may also want to include more of the other + headers. + */ + +#include +#include +#include +#include + +#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 + * 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 header is deprecated in Libevent 2.0 and later; please + use instead. Depending on what functionality you + need, you may also want to include more of the other + headers. + */ + +#include +#include +#include +#include + +#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 header is deprecated in Libevent 2.0 and later; please + use instead. +*/ + +#include + +#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 -/// 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: +/// +///
    +///
  • @c kFIRParameterCoupon (NSString) (optional)
  • +///
  • @c kFIRParameterCurrency (NSString) (optional)
  • +///
  • @c kFIRParameterItems (NSArray) (optional)
  • +///
  • @c kFIRParameterPaymentType (NSString) (optional)
  • +///
  • @c kFIRParameterValue (double as NSNumber) (optional)
  • +///
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: /// ///
    -///
  • @c kFIRParameterQuantity (signed 64-bit integer as NSNumber)
  • -///
  • @c kFIRParameterItemID (NSString)
  • -///
  • @c kFIRParameterItemName (NSString)
  • -///
  • @c kFIRParameterItemCategory (NSString)
  • -///
  • @c kFIRParameterItemLocationID (NSString) (optional)
  • -///
  • @c kFIRParameterPrice (double as NSNumber) (optional)
  • ///
  • @c kFIRParameterCurrency (NSString) (optional)
  • +///
  • @c kFIRParameterItems (NSArray) (optional)
  • ///
  • @c kFIRParameterValue (double as NSNumber) (optional)
  • -///
  • @c kFIRParameterOrigin (NSString) (optional)
  • -///
  • @c kFIRParameterDestination (NSString) (optional)
  • -///
  • @c kFIRParameterStartDate (NSString) (optional)
  • -///
  • @c kFIRParameterEndDate (NSString) (optional)
  • ///
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: /// ///
    -///
  • @c kFIRParameterQuantity (signed 64-bit integer as NSNumber)
  • -///
  • @c kFIRParameterItemID (NSString)
  • -///
  • @c kFIRParameterItemName (NSString)
  • -///
  • @c kFIRParameterItemCategory (NSString)
  • -///
  • @c kFIRParameterItemLocationID (NSString) (optional)
  • -///
  • @c kFIRParameterPrice (double as NSNumber) (optional)
  • ///
  • @c kFIRParameterCurrency (NSString) (optional)
  • +///
  • @c kFIRParameterItems (NSArray) (optional)
  • ///
  • @c kFIRParameterValue (double as NSNumber) (optional)
  • ///
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: /// ///
    -///
  • @c kFIRParameterValue (double as NSNumber) (optional)
  • +///
  • @c kFIRParameterCoupon (NSString) (optional)
  • ///
  • @c kFIRParameterCurrency (NSString) (optional)
  • -///
  • @c kFIRParameterTransactionID (NSString) (optional)
  • -///
  • @c kFIRParameterStartDate (NSString) (optional)
  • -///
  • @c kFIRParameterEndDate (NSString) (optional)
  • -///
  • @c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • @c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • @c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings
  • -///
  • @c kFIRParameterOrigin (NSString) (optional)
  • -///
  • @c kFIRParameterDestination (NSString) (optional)
  • -///
  • @c kFIRParameterTravelClass (NSString) (optional) for travel bookings
  • +///
  • @c kFIRParameterItems (NSArray) (optional)
  • +///
  • @c kFIRParameterValue (double as NSNumber) (optional)
  • ///
static NSString *const kFIREventBeginCheckout NS_SWIFT_NAME(AnalyticsEventBeginCheckout) = @"begin_checkout"; @@ -110,6 +95,7 @@ static NSString *const kFIREventCampaignDetails NS_SWIFT_NAME(AnalyticsEventCamp ///
  • @c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)
  • ///
  • @c kFIRParameterCheckoutOption (NSString) (optional)
  • /// +/// This constant has been deprecated. static NSString *const kFIREventCheckoutProgress NS_SWIFT_NAME(AnalyticsEventCheckoutProgress) = @"checkout_progress"; @@ -150,6 +136,7 @@ static NSString *const kFIREventEarnVirtualCurrency ///
  • @c kFIRParameterDestination (NSString) (optional)
  • ///
  • @c kFIRParameterTravelClass (NSString) (optional) for travel bookings
  • /// +/// This constant has been deprecated. Use @c kFIREventPurchase constant instead. static NSString *const kFIREventEcommercePurchase NS_SWIFT_NAME(AnalyticsEventEcommercePurchase) = @"ecommerce_purchase"; @@ -173,6 +160,21 @@ static NSString *const kFIREventGenerateLead NS_SWIFT_NAME(AnalyticsEventGenerat /// static NSString *const kFIREventJoinGroup NS_SWIFT_NAME(AnalyticsEventJoinGroup) = @"join_group"; +/// Level End event. Log this event when the user finishes a level. Params: +/// +///
      +///
    • @c kFIRParameterLevelName (NSString)
    • +///
    • @c kFIRParameterSuccess (NSString)
    • +///
    +static NSString *const kFIREventLevelEnd NS_SWIFT_NAME(AnalyticsEventLevelEnd) = @"level_end"; + +/// Level Start event. Log this event when the user starts a new level. Params: +/// +///
      +///
    • @c kFIRParameterLevelName (NSString)
    • +///
    +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) ///
  • @c kFIRParameterCurrency (NSString) (optional)
  • ///
  • @c kFIRParameterValue (double as NSNumber) (optional)
  • /// +/// This constant has been deprecated. Use @c kFIREventViewPromotion constant instead. static NSString *const kFIREventPresentOffer NS_SWIFT_NAME(AnalyticsEventPresentOffer) = @"present_offer"; @@ -227,24 +230,18 @@ static NSString *const kFIREventPresentOffer NS_SWIFT_NAME(AnalyticsEventPresent ///
  • @c kFIRParameterValue (double as NSNumber) (optional)
  • ///
  • @c kFIRParameterTransactionID (NSString) (optional)
  • /// +/// This constant has been deprecated. Use @c kFIREventRefund constant instead. 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: /// ///
      -///
    • @c kFIRParameterQuantity (signed 64-bit integer as NSNumber)
    • -///
    • @c kFIRParameterItemID (NSString)
    • -///
    • @c kFIRParameterItemName (NSString)
    • -///
    • @c kFIRParameterItemCategory (NSString)
    • -///
    • @c kFIRParameterItemLocationID (NSString) (optional)
    • -///
    • @c kFIRParameterPrice (double as NSNumber) (optional)
    • ///
    • @c kFIRParameterCurrency (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • ///
    • @c kFIRParameterValue (double as NSNumber) (optional)
    • -///
    • @c kFIRParameterOrigin (NSString) (optional)
    • -///
    • @c kFIRParameterDestination (NSString) (optional)
    • -///
    • @c kFIRParameterStartDate (NSString) (optional)
    • -///
    • @c kFIRParameterEndDate (NSString) (optional)
    • ///
    static NSString *const kFIREventRemoveFromCart NS_SWIFT_NAME(AnalyticsEventRemoveFromCart) = @"remove_from_cart"; @@ -286,6 +283,7 @@ static NSString *const kFIREventSelectContent NS_SWIFT_NAME(AnalyticsEventSelect ///
  • @c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)
  • ///
  • @c kFIRParameterCheckoutOption (NSString)
  • /// +/// This constant has been deprecated. 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: /// ///
      -///
    • @c kFIRParameterItemID (NSString)
    • -///
    • @c kFIRParameterItemName (NSString)
    • -///
    • @c kFIRParameterItemCategory (NSString)
    • -///
    • @c kFIRParameterItemLocationID (NSString) (optional)
    • -///
    • @c kFIRParameterPrice (double as NSNumber) (optional)
    • -///
    • @c kFIRParameterQuantity (signed 64-bit integer as NSNumber) (optional)
    • ///
    • @c kFIRParameterCurrency (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • ///
    • @c kFIRParameterValue (double as NSNumber) (optional)
    • -///
    • @c kFIRParameterStartDate (NSString) (optional)
    • -///
    • @c kFIRParameterEndDate (NSString) (optional)
    • -///
    • @c kFIRParameterFlightNumber (NSString) (optional) for travel bookings
    • -///
    • @c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings
    • -///
    • @c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for -/// travel bookings
    • -///
    • @c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for -/// travel bookings
    • -///
    • @c kFIRParameterOrigin (NSString) (optional)
    • -///
    • @c kFIRParameterDestination (NSString) (optional)
    • -///
    • @c kFIRParameterSearchTerm (NSString) (optional) for travel bookings
    • -///
    • @c kFIRParameterTravelClass (NSString) (optional) for travel bookings
    • ///
    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: /// ///
      -///
    • @c kFIRParameterItemCategory (NSString)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterItemListID (NSString) (optional)
    • +///
    • @c kFIRParameterItemListName (NSString) (optional)
    • ///
    static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewItemList) = @"view_item_list"; @@ -387,21 +366,107 @@ static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewIte ///
      ///
    • @c kFIRParameterSearchTerm (NSString)
    • ///
    +/// This constant has been deprecated. 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: /// ///
      -///
    • @c kFIRParameterLevelName (NSString)
    • +///
    • @c kFIRParameterCoupon (NSString) (optional)
    • +///
    • @c kFIRParameterCurrency (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterShippingTier (NSString) (optional)
    • +///
    • @c kFIRParameterValue (double as NSNumber) (optional)
    • ///
    -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: /// ///
      -///
    • @c kFIRParameterLevelName (NSString)
    • -///
    • @c kFIRParameterSuccess (NSString)
    • +///
    • @c kFIRParameterAffiliation (NSString) (optional)
    • +///
    • @c kFIRParameterCoupon (NSString) (optional)
    • +///
    • @c kFIRParameterCurrency (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterShipping (double as NSNumber) (optional)
    • +///
    • @c kFIRParameterTax (double as NSNumber) (optional)
    • +///
    • @c kFIRParameterTransactionID (NSString) (optional)
    • +///
    • @c kFIRParameterValue (double as NSNumber) (optional)
    • ///
    -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: +/// +///
      +///
    • @c kFIRParameterAffiliation (NSString) (optional)
    • +///
    • @c kFIRParameterCoupon (NSString) (optional)
    • +///
    • @c kFIRParameterCurrency (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterShipping (double as NSNumber) (optional)
    • +///
    • @c kFIRParameterTax (double as NSNumber) (optional)
    • +///
    • @c kFIRParameterTransactionID (NSString) (optional)
    • +///
    • @c kFIRParameterValue (double as NSNumber) (optional)
    • +///
    +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: +/// +///
      +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterItemListID (NSString) (optional)
    • +///
    • @c kFIRParameterItemListName (NSString) (optional)
    • +///
    +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: +/// +///
      +///
    • @c kFIRParameterCreativeName (NSString) (optional)
    • +///
    • @c kFIRParameterCreativeSlot (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterLocationID (NSString) (optional)
    • +///
    • @c kFIRParameterPromotionID (NSString) (optional)
    • +///
    • @c kFIRParameterPromotionName (NSString) (optional)
    • +///
    +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: +/// +///
      +///
    • @c kFIRParameterCurrency (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterValue (double as NSNumber) (optional)
    • +///
    +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: +/// +///
      +///
    • @c kFIRParameterCreativeName (NSString) (optional)
    • +///
    • @c kFIRParameterCreativeSlot (NSString) (optional)
    • +///
    • @c kFIRParameterItems (NSArray) (optional)
    • +///
    • @c kFIRParameterLocationID (NSString) (optional)
    • +///
    • @c kFIRParameterPromotionID (NSString) (optional)
    • +///
    • @c kFIRParameterPromotionName (NSString) (optional)
    • +///
    +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). -///
    +/// A product affiliation to designate a supplying company or brick and mortar store location
    +/// (NSString). 
     ///     NSDictionary *params = @{
     ///       kFIRParameterAffiliation : @"Google Store",
     ///       // ...
    @@ -71,6 +71,7 @@ static NSString *const kFIRParameterCharacter NS_SWIFT_NAME(AnalyticsParameterCh
     ///       // ...
     ///     };
     /// 
    +/// This constant has been deprecated. static NSString *const kFIRParameterCheckoutStep NS_SWIFT_NAME(AnalyticsParameterCheckoutStep) = @"checkout_step"; @@ -81,6 +82,7 @@ static NSString *const kFIRParameterCheckoutStep NS_SWIFT_NAME(AnalyticsParamete /// // ... /// }; ///
    +/// This constant has been deprecated. 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). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterCoupon : @"zz123",
    +///       kFIRParameterCoupon : @"SUMMER_FUN",
     ///       // ...
     ///     };
     /// 
    @@ -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 -/// ISO_4217 format (NSString). +/// Currency of the purchase or items associated with the event, in 3-letter +/// ISO_4217 format (NSString). ///
     ///     NSDictionary *params = @{
     ///       kFIRParameterCurrency : @"USD",
    @@ -186,10 +188,10 @@ static NSString *const kFIRParameterFlightNumber NS_SWIFT_NAME(AnalyticsParamete
     /// 
    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). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterIndex : @(1),
    +///       kFIRParameterIndex : @(5),
     ///       // ...
     ///     };
     /// 
    @@ -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). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterItemCategory : @"t-shirts",
    +///       kFIRParameterItemCategory : @"pants",
     ///       // ...
     ///     };
     /// 
    static NSString *const kFIRParameterItemCategory NS_SWIFT_NAME(AnalyticsParameterItemCategory) = @"item_category"; -/// Item ID (NSString). +/// Item ID (context-specific) (NSString). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterItemID : @"p7654",
    +///       kFIRParameterItemID : @"SKU_12345",
     ///       // ...
     ///     };
     /// 
    @@ -232,13 +234,14 @@ static NSString *const kFIRParameterItemID NS_SWIFT_NAME(AnalyticsParameterItemI /// // ... /// }; /// +/// This constant has been deprecated. Use @c kFIRParameterLocationID constant instead. static NSString *const kFIRParameterItemLocationID NS_SWIFT_NAME(AnalyticsParameterItemLocationID) = @"item_location_id"; -/// Item name (NSString). +/// Item Name (context-specific) (NSString). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterItemName : @"abc",
    +///       kFIRParameterItemName : @"jeggings",
     ///       // ...
     ///     };
     /// 
    @@ -252,13 +255,14 @@ static NSString *const kFIRParameterItemName NS_SWIFT_NAME(AnalyticsParameterIte /// // ... /// }; /// +/// This constant has been deprecated. Use @c kFIRParameterItemListName constant instead. static NSString *const kFIRParameterItemList NS_SWIFT_NAME(AnalyticsParameterItemList) = @"item_list"; /// Item variant (NSString). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterItemVariant : @"Red",
    +///       kFIRParameterItemVariant : @"Black",
     ///       // ...
     ///     };
     /// 
    @@ -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). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterShipping : @(9.50),
    -///       kFIRParameterCurrency : @"USD",  // e.g. $9.50 USD
    +///       kFIRParameterShipping : @(5.99),
    +///       kFIRParameterCurrency : @"USD",  // e.g. $5.99 USD
     ///       // ...
     ///     };
     /// 
    @@ -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). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterTax : @(1.0),
    -///       kFIRParameterCurrency : @"USD",  // e.g. $1.00 USD
    +///       kFIRParameterTax : @(2.43),
    +///       kFIRParameterCurrency : @"USD",  // e.g. $2.43 USD
     ///       // ...
     ///     };
     /// 
    @@ -449,10 +453,10 @@ static NSString *const kFIRParameterTax NS_SWIFT_NAME(AnalyticsParameterTax) = @ /// 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). ///
     ///     NSDictionary *params = @{
    -///       kFIRParameterTransactionID : @"ab7236dd9823",
    +///       kFIRParameterTransactionID : @"T12345",
     ///       // ...
     ///     };
     /// 
    @@ -530,3 +534,137 @@ static NSString *const kFIRParameterSuccess NS_SWIFT_NAME(AnalyticsParameterSucc /// static NSString *const kFIRParameterExtendSession NS_SWIFT_NAME(AnalyticsParameterExtendSession) = @"extend_session"; + +/// Monetary value of discount associated with a purchase (double as NSNumber). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterDiscount : @(2.0),
    +///       kFIRParameterCurrency : @"USD",  // e.g. $2.00 USD
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterDiscount NS_SWIFT_NAME(AnalyticsParameterDiscount) = + @"discount"; + +/// Item Category (context-specific) (NSString). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterItemCategory2 : @"pants",
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterItemCategory2 NS_SWIFT_NAME(AnalyticsParameterItemCategory2) = + @"item_category2"; + +/// Item Category (context-specific) (NSString). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterItemCategory3 : @"pants",
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterItemCategory3 NS_SWIFT_NAME(AnalyticsParameterItemCategory3) = + @"item_category3"; + +/// Item Category (context-specific) (NSString). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterItemCategory4 : @"pants",
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterItemCategory4 NS_SWIFT_NAME(AnalyticsParameterItemCategory4) = + @"item_category4"; + +/// Item Category (context-specific) (NSString). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterItemCategory5 : @"pants",
    +///       // ...
    +///     };
    +/// 
    +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). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterItemListID : @"ABC123",
    +///       // ...
    +///     };
    +/// 
    +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). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterItemListName : @"Related products",
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterItemListName NS_SWIFT_NAME(AnalyticsParameterItemListName) = + @"item_list_name"; + +/// The list of items involved in the transaction. (NSArray). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterItems : @[
    +///         @{kFIRParameterItemName : @"jeggings", kFIRParameterItemCategory : @"pants"},
    +///         @{kFIRParameterItemName : @"boots", kFIRParameterItemCategory : @"shoes"},
    +///       ],
    +///     };
    +/// 
    +static NSString *const kFIRParameterItems NS_SWIFT_NAME(AnalyticsParameterItems) = @"items"; + +/// The location associated with the event. Preferred to be the Google +/// Place ID that corresponds to the +/// associated item but could be overridden to a custom location ID string.(NSString).
    +///     NSDictionary *params = @{
    +///       kFIRParameterLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterLocationID NS_SWIFT_NAME(AnalyticsParameterLocationID) = + @"location_id"; + +/// The chosen method of payment (NSString). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterPaymentType : @"Visa",
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterPaymentType NS_SWIFT_NAME(AnalyticsParameterPaymentType) = + @"payment_type"; + +/// The ID of a product promotion (NSString). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterPromotionID : @"ABC123",
    +///       // ...
    +///     };
    +/// 
    +static NSString *const kFIRParameterPromotionID NS_SWIFT_NAME(AnalyticsParameterPromotionID) = + @"promotion_id"; + +/// The name of a product promotion (NSString). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterPromotionName : @"Summer Sale",
    +///       // ...
    +///     };
    +/// 
    +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). +///
    +///     NSDictionary *params = @{
    +///       kFIRParameterShippingTier : @"Ground",
    +///       // ...
    +///     };
    +/// 
    +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 *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 *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 #import -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)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 + #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 *)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 *)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 *( FIRInstallationsURLSessionResponse *response) { return [self authTokenWithServerResponse:response]; @@ -115,17 +119,20 @@ NS_ASSUME_NONNULL_END } - (FBLPromise *)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 *)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 *)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 *)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 *)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 *)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 *)requestWithURL:(NSURL *)requestURL + HTTPMethod:(NSString *)HTTPMethod + bodyDict:(NSDictionary *)bodyDict + refreshToken:(nullable NSString *)refreshToken + additionalHeaders:(nullable NSDictionary *) + 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 *)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 +#import #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; @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 + #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 - -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 - -// 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 - -#import -#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 - -#import -#import -#import -#import -#import -#import -#import -#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 *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 () -@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)self - withName:@"fire-iid" - withVersion:FIRInstanceIDCurrentLibraryVersion()]; -} - -+ (nonnull NSArray *)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 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 *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:@"" intoString:&plistContents]) { - plistContents = [plistContents stringByAppendingString:@""]; - } - } - - 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 - -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 - -/// 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 - -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 *)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 *> *> - *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 *)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 *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 *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 -#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 *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 - -@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 - -@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 -#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 - -#import -#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 - -@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: , -// 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 () -@property(atomic, readonly, strong) NSMutableArray *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 *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 - -#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 - -/* 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 - -// 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 - -NS_ASSUME_NONNULL_BEGIN - -@class FIRInstanceIDBackupExcludedPlist; -@class FIRInstanceIDCheckinPreferences; -@class FIRInstanceIDCheckinStore; -@class FIRInstanceIDTokenInfo; -@class FIRInstanceIDTokenStore; - -@class FIRInstanceIDStore; -@protocol FIRInstanceIDStoreDelegate - -/** - * 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 *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 *)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 *)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 *)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 *)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 *)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 *)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 - -// 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:@"", 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 *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 *)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 -#import - -// 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 *)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 *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=\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 - -#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 - -/// 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 *)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 () - -@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 *)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 *tokenInfos = [self.instanceIDStore cachedTokenInfos]; - - NSMutableArray *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 *)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 *tokenInfos = [self.instanceIDStore cachedTokenInfos]; - NSMutableArray *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 *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 *)options - checkinPreferences:(FIRInstanceIDCheckinPreferences *)checkinPreferences - instanceID:(NSString *)instanceID; - -#pragma mark - Request Construction -+ (NSMutableArray *)standardQueryItemsWithDeviceID:(NSString *)deviceID - scope:(NSString *)scope; -- (NSMutableURLRequest *)tokenRequest; -- (NSArray *)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 - -@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 *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 - -#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 *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 *)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 *)standardQueryItemsWithDeviceID:(NSString *)deviceID - scope:(NSString *)scope { - NSMutableArray *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 *)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 - -@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:
    (e.g. com.mycompany.myapp) - * Service: : (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 *)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 *)cachedTokenInfos { - NSString *account = FIRInstanceIDAppIdentifier(); - NSArray *items = - [self.keychain itemsMatchingService:kFIRInstanceIDKeychainWildcardIdentifier account:account]; - NSMutableArray *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:
    (e.g. com.mycompany.myapp) -// Service: : (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 - -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 *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 *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 *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 *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 - -/// 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 -#endif -#import - -#import -#import -#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 *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 - -/** - * 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_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 -#import - -/** - * @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 - -/** - * 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 - -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 - -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 - -/** - * 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 + +#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 buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector(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(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector 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(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 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(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 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(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(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(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(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 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 +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 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 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 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(factor) * bigits_[i] + carry; + bigits_[i] = static_cast(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(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast(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(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(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(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(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(quotient); + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + ASSERT(division_estimate < 0x10000); + result += static_cast(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 +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(value + '0'); + return static_cast(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(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((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 value); + void AssignHexString(Vector 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 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 +#include +#include + +#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(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 +#include + +#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 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 +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(*current) - '0'; + } else if (IsCharacterDigitForRadix(*current, radix, 'a')) { + digit = static_cast(*current) - 'a' + 10; + } else if (IsCharacterDigitForRadix(*current, radix, 'A')) { + digit = static_cast(*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(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(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(static_cast(number)) == number); + + *trailing_pointer = current; + + if (exponent == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast(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(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: "-.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(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(current - input); + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast(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(tail_pointer - input); + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast(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(*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(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(*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(*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(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(buffer, buffer_pos), exponent); + } else { + converted = Strtof(Vector(buffer, buffer_pos), exponent); + } + *processed_characters_count = static_cast(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(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(StringToDouble(...)) + // due to potential double-rounding. + float StringToFloat(const char* buffer, + int length, + int* processed_characters_count) const { + return static_cast(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 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 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 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(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(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('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(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(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(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast('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 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(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(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('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(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast(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(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast('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 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(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 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 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 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 + +#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(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(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(high_bits_ >> (power - 64)); + high_bits_ -= static_cast(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(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(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast(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 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 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('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 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(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); +} + + +static void FillDigits64(uint64_t number, Vector 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(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(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 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 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(fractionals >> point); + ASSERT(digit <= 9); + buffer[*length] = static_cast('0' + digit); + (*length)++; + fractionals -= static_cast(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('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 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 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(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast(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(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 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(d); } +static double uint64_to_double(uint64_t d64) { return BitCast(d64); } +static uint32_t float_to_uint32(float f) { return BitCast(f); } +static float uint32_to_float(uint32_t d32) { return BitCast(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((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(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((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 +#include + +#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 TrimLeadingZeros(Vector buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector(buffer.start(), 0); +} + + +static Vector TrimTrailingZeros(Vector buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector(buffer.start(), 0); +} + + +static void CutToMaxSignificantDigits(Vector 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 buffer, int exponent, + char* buffer_copy_space, int space_size, + Vector* trimmed, int* updated_exponent) { + Vector left_trimmed = TrimLeadingZeros(buffer); + Vector 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(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 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 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 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(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(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(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 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 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 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 buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector 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 buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector 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(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(double_previous); + float f2 = float_guess; + float f3 = static_cast(double_next); + float f4; + if (is_correct) { + f4 = f3; + } else { + double double_next2 = Double(double_next).NextDouble(); + f4 = static_cast(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(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 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 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 +#include + +#include +#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 + +#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(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(!(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 +static T Max(T a, T b) { + return a < b ? b : a; +} + + +// Returns the minimum of the two parameters. +template +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(static_cast(length))); + return static_cast(length); +} + +// This is a simplified version of V8's Vector class. +template +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 SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector(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(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(position_)); + position_ = -1; + ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector 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 +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 +inline Dest BitCast(Source* source) { + return BitCast(reinterpret_cast(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 `. + +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 + +#include +#include +#include +#include + +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("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 +typename AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::SimpleRetT +AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::findInternal(const LookupKeyT key_in) { + checkLegalKeyIfKey(key_in); + + for (size_t idx = keyToAnchorIdx(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(key_in); + + size_t idx = keyToAnchorIdx(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::type LookupKeyTNoConst; + constexpr bool kAlreadyChecked = + std::is_same::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::type; + new (const_cast(&cell->second)) + ValueT(std::forward(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((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(&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 +struct AtomicHashArray< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::aha_iterator + : detail::IteratorFacade< + aha_iterator, + 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 + aha_iterator( + const aha_iterator& o, + typename std::enable_if< + std::is_convertible::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; + + 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 + * @author Jordan DeLong + */ + +#pragma once +#define FOLLY_ATOMICHASHARRAY_H_ + +#include + +#include +#include +#include + +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 +inline void checkLegalKeyIfKeyTImpl( + NotKeyT /* ignored */, + KeyT /* emptyKey */, + KeyT /* lockedKey */, + KeyT /* erasedKey */) {} + +template +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, + class EqualFcn = std::equal_to, + class Allocator = std::allocator, + class ProbeFcn = AtomicHashArrayLinearProbeFcn, + class KeyConvertFcn = Identity> +class AtomicHashMap; + +template < + class KeyT, + class ValueT, + class HashFcn = std::hash, + class EqualFcn = std::equal_to, + class Allocator = std::allocator, + class ProbeFcn = AtomicHashArrayLinearProbeFcn, + class KeyConvertFcn = Identity> +class AtomicHashArray { + static_assert( + (std::is_convertible::value || + std::is_convertible::value || + std::is_convertible::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 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 + struct aha_iterator; + + typedef aha_iterator const_iterator; + typedef aha_iterator 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 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(k).idx); + } + + template < + typename LookupKeyT = key_type, + typename LookupHashFcn = hasher, + typename LookupEqualFcn = key_equal> + const_iterator find(LookupKeyT k) const { + return const_cast(this) + ->find(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 insert(const value_type& r) { + return emplace(r.first, r.second); + } + std::pair 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 emplace(LookupKeyT key_in, ArgTs&&... vCtorArgs) { + SimpleRetT ret = insertInternal< + LookupKeyT, + LookupHashFcn, + LookupEqualFcn, + LookupKeyToKeyFcn>(key_in, std::forward(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(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 + void checkLegalKeyIfKey(MaybeKeyT key) { + detail::checkLegalKeyIfKeyTImpl(key, kEmptyKey_, kLockedKey_, kErasedKey_); + } + + static std::atomic* cellKeyPtr(const value_type& r) { + // We need some illegal casting here in order to actually store + // our value_type as a std::pair. But a little bit of + // undefined behavior never hurt anyone ... + static_assert( + sizeof(std::atomic) == sizeof(KeyT), + "std::atomic is implemented in an unexpected way for AHM"); + return const_cast*>( + reinterpret_cast 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 numEntries_; // Successful key inserts + ThreadCachedInt numPendingEntries_; // Used by insertInternal + std::atomic isFull_; // Used by insertInternal + std::atomic 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 + 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 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 +#include + +#include + +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(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(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(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 +typename AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::iterator +AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::find(LookupKeyT k) { + SimpleRetT ret = findInternal(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 +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(this) + ->find(k); +} + +// findInternal -- +template < + typename KeyT, + typename ValueT, + typename HashFcn, + typename EqualFcn, + typename Allocator, + typename ProbeFcn, + typename KeyConvertFcn> +template +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(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( + 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 +struct AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + ProbeFcn, + KeyConvertFcn>::ahm_iterator + : detail::IteratorFacade< + ahm_iterator, + 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 + ahm_iterator( + const ahm_iterator& o, + typename std::enable_if< + std::is_convertible::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; + + 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 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 + * @author Jordan DeLong + * + */ + +#pragma once +#define FOLLY_ATOMICHASHMAP_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 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 + struct ahm_iterator; + + typedef ahm_iterator< + const AtomicHashMap, + const value_type, + typename SubMap::const_iterator> + const_iterator; + typedef ahm_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 insert(const value_type& r) { + return emplace(r.first, r.second); + } + std::pair insert(key_type k, const mapped_type& v) { + return emplace(k, v); + } + std::pair insert(value_type&& r) { + return emplace(r.first, std::move(r.second)); + } + std::pair 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 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(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 subMaps_[kNumSubMaps_]; + std::atomic 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, + class EqualFcn = std::equal_to, + class Allocator = std::allocator> +using QuadraticProbingAtomicHashMap = AtomicHashMap< + KeyT, + ValueT, + HashFcn, + EqualFcn, + Allocator, + AtomicHashArrayQuadraticProbeFcn>; +} // namespace folly + +#include 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 +#include +#include + +namespace folly { + +/** + * A very simple atomic single-linked list primitive. + * + * Usage: + * + * class MyClass { + * AtomicIntrusiveLinkedListHook hook_; + * } + * + * AtomicIntrusiveLinkedList list; + * list.insert(&a); + * list.sweep([] (MyClass* c) { doSomething(c); } + */ +template +struct AtomicIntrusiveLinkedListHook { + T* next{nullptr}; +}; + +template 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 + bool sweepOnce(F&& func) { + if (auto head = head_.exchange(nullptr)) { + auto rhead = reverse(head); + unlinkAll(rhead, std::forward(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 + 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 + 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(func)); + } + + private: + std::atomic 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 + 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 +#include + +namespace folly { + +/** + * A very simple atomic single-linked list primitive. + * + * Usage: + * + * AtomicLinkedList list; + * list.insert(a); + * list.sweep([] (MyClass& c) { doSomething(c); } + */ + +template +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(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 + void sweep(F&& func) { + list_.sweep([&](Wrapper* wrapperPtr) mutable { + std::unique_ptr 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 + void reverseSweep(F&& func) { + list_.reverseSweep([&](Wrapper* wrapperPtr) mutable { + std::unique_ptr wrapper(wrapperPtr); + + func(std::move(wrapper->data)); + }); + } + + private: + struct Wrapper { + explicit Wrapper(T&& t) : data(std::move(t)) {} + + AtomicIntrusiveLinkedListHook hook; + T data; + }; + AtomicIntrusiveLinkedList 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace folly { + +/// You're probably reading this because you are looking for an +/// AtomicUnorderedMap 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, + typename KeyEqual = std::equal_to, + bool SkipKeyValueDeletion = + (std::is_trivially_destructible::value && + std::is_trivially_destructible::value), + template 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 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(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(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 memo; + /// + /// auto value = memo.findOrConstruct(key, [=](void* raw) { + /// new (raw) std::string(computation(key)); + /// })->first; + template + std::pair 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(&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 + std::pair emplace(const K& key, V&& value) { + return findOrConstruct( + key, [&](void* raw) { new (raw) Value(std::forward(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 headAndState_; + + /// The next bucket in the chain + IndexType next_; + + /// Key and Value + aligned_storage_for_t 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(static_cast(&raw_)); + } + + const value_type& keyValue() const { + assert(state() != EMPTY); + return *static_cast(static_cast(&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::value) { + memset(static_cast(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, + typename KeyEqual = std::equal_to, + bool SkipKeyValueDeletion = + (std::is_trivially_destructible::value && + std::is_trivially_destructible::value), + template 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>. This relies on AtomicUnorderedInsertMap's guarantee +/// that it doesn't move values. +template class Atom = std::atomic> +struct MutableAtom { + mutable Atom 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 +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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +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 BenchmarkFun; + +vector& benchmarks() { + static vector _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 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::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(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> 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(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(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(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 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& 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::infinity() + : (1 / secPerIter); + if (!useBaseline) { + // Print without baseline + printf( + "%*s %9s %7s", + static_cast(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(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 counterNames_; + size_t namesLength_{0}; + double baselineNsPerIter_{numeric_limits::max()}; + string lastFile_; +}; +} // namespace + +static void printBenchmarkResultsAsJson( + const vector& 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& data) { + dynamic d; + benchmarkResultsToDynamic(data, d); + printf("%s\n", toPrettyJson(d).c_str()); +} + +static void printBenchmarkResults(const vector& 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& 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(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& results) { + for (auto& datum : d) { + results.push_back({datum[0].asString(), + datum[1].asString(), + datum[2].asDouble(), + UserCounters{}}); + } +} + +static pair resultKey( + const detail::BenchmarkResult& result) { + return pair(result.file, result.name); +} + +void printResultComparison( + const vector& base, + const vector& test) { + map, 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 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::infinity() + : (1 / secPerIter); + if (!baseline) { + // Print without baseline + printf( + "%*s %9s %7s\n", + static_cast(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(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 results; + results.reserve(benchmarks().size() - 1); + + std::unique_ptr bmRegex; + if (!FLAGS_bm_regex.empty()) { + bmRegex = std::make_unique(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 counterNames; + FOR_EACH_RANGE (i, 0, benchmarks().size()) { + if (i == baselineIndex) { + continue; + } + std::pair 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 +#include // for FB_ANONYMOUS_VARIABLE +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +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; + +namespace detail { +struct TimeIterData { + std::chrono::high_resolution_clock::duration duration; + unsigned int niter; + UserCounters userCounters; +}; + +using BenchmarkFun = std::function; + +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 + auto dismissing(F f) -> invoke_result_t { + 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 std::enable_if>::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 std::enable_if>::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 std::enable_if< + folly::is_invocable_v>::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(execute), + true); +} + +template +typename std::enable_if>::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 +void doNotOptimizeAway(const T& datum) { + doNotOptimizeDependencySink(&datum); +} + +template +void makeUnpredictable(T& datum) { + doNotOptimizeDependencySink(&datum); +} + +#else + +namespace detail { +template +struct DoNotOptimizeAwayNeedsIndirect { + using Decayed = typename std::decay::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::value || + sizeof(Decayed) > sizeof(long) || std::is_pointer::value; +}; +} // namespace detail + +template +auto doNotOptimizeAway(const T& datum) -> typename std::enable_if< + !detail::DoNotOptimizeAwayNeedsIndirect::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 +auto doNotOptimizeAway(const T& datum) -> typename std::enable_if< + detail::DoNotOptimizeAwayNeedsIndirect::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 +auto makeUnpredictable(T& datum) -> typename std::enable_if< + !detail::DoNotOptimizeAwayNeedsIndirect::value>::type { + asm volatile("" : "+r"(datum)); +} + +template +auto makeUnpredictable(T& datum) -> typename std::enable_if< + detail::DoNotOptimizeAwayNeedsIndirect::value>::type { + asm volatile("" ::"m"(datum) : "memory"); +} + +#endif + +struct dynamic; + +void benchmarkResultsToDynamic( + const std::vector& data, + dynamic&); + +void benchmarkResultsFromDynamic( + const dynamic&, + std::vector&); + +void printResultComparison( + const std::vector& base, + const std::vector& 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 v; + * v.push_back(42); + * } + * + * BENCHMARK(insertVectorBegin, iters) { + * vector 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 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 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 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 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 v; + * FOR_EACH_RANGE (i, 0, n) { + * v.insert(v.begin(), 42); + * } + * } + * + * BENCHMARK_RELATIVE(insertListBegin, n) { + * list 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 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 // @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 + +/** + * 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 +#include +#include +#include + +#include + +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 + 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::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 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:: + value, + int>> +inline CancellationCallback::CancellationCallback( + CancellationToken&& ct, + Callable&& callable) + : next_(nullptr), + prevNext_(nullptr), + state_(nullptr), + callback_(static_cast(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:: + value, + int>> +inline CancellationCallback::CancellationCallback( + const CancellationToken& ct, + Callable&& callable) + : next_(nullptr), + prevNext_(nullptr), + state_(nullptr), + callback_(static_cast(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 +#include +#include + +#include + +#include +#include +#include +#include + +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 +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 +#include + +#include +#include +#include +#include + +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; +using CancellationStateSourcePtr = + std::unique_ptr; +} // 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 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; + + 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::value, + int> = 0> + CancellationCallback(CancellationToken&& ct, Callable&& callable); + template < + typename Callable, + std::enable_if_t< + std::is_constructible::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 callbackCompleted_; +}; + +} // namespace folly + +#include 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 +#include +#include + +#include +#include +#include + +/*** + * 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 +struct is_duration : std::false_type {}; +template +struct is_duration> : std::true_type {}; + +template +constexpr To ceil_impl(Duration const& d, To const& t) { + return t < d ? t + To{1} : t; +} + +template +constexpr To floor_impl(Duration const& d, To const& t) { + return t > d ? t - To{1} : t; +} + +template +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 +constexpr To round_impl(Duration const& d, To const& t0, To const& t1) { + return round_impl(t0, t1, d - t0, t1 - d); +} + +template +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::min() < + std::chrono::duration::zero()>::type> +constexpr std::chrono::duration abs( + std::chrono::duration const& d) { + return d < std::chrono::duration::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::value>::type> +constexpr To ceil(std::chrono::duration const& d) { + return detail::ceil_impl(d, std::chrono::duration_cast(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::value>::type> +constexpr std::chrono::time_point ceil( + std::chrono::time_point const& tp) { + return std::chrono::time_point{ceil(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::value>::type> +constexpr To floor(std::chrono::duration const& d) { + return detail::floor_impl(d, std::chrono::duration_cast(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::value>::type> +constexpr std::chrono::time_point floor( + std::chrono::time_point const& tp) { + return std::chrono::time_point{floor(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::value && + !std::chrono::treat_as_floating_point::value>::type> +constexpr To round(std::chrono::duration const& d) { + return detail::round_impl(d, floor(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::value && + !std::chrono::treat_as_floating_point::value>::type> +constexpr std::chrono::time_point round( + std::chrono::time_point const& tp) { + return std::chrono::time_point{round(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; + using time_point = std::chrono::time_point; + constexpr static bool is_steady = true; + + static time_point now() noexcept { +#ifndef CLOCK_MONOTONIC_COARSE + return time_point(std::chrono::duration_cast( + 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( + "Error using CLOCK_MONOTONIC_COARSE."); + } + + return time_point(std::chrono::duration_cast( + 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 +#include +#include +#include + +#include + +#include + +#ifndef _WIN32 +#define _GNU_SOURCE 1 +#include +#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 +#pragma once -/** Checkin refresh interval. **/ -FOUNDATION_EXPORT const NSTimeInterval kFIRInstanceIDDefaultCheckinInterval; +#include -@interface FIRInstanceIDCheckinPreferences () +#include -- (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 +#include +#include +#include +#include + +#include + +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 +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 AtomicBlockType; + + static constexpr size_t kBitsPerBlock = + std::numeric_limits::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 data_; +}; + +// value-initialize to zero +template +inline ConcurrentBitSet::ConcurrentBitSet() : data_() {} + +template +inline bool ConcurrentBitSet::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 +inline bool ConcurrentBitSet::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 +inline bool +ConcurrentBitSet::set(size_t idx, bool value, std::memory_order order) { + return value ? set(idx, order) : reset(idx, order); +} + +template +inline bool ConcurrentBitSet::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 +inline bool ConcurrentBitSet::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 + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace folly { +namespace detail { + +template +class csl_iterator; + +template +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::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); + auto storage = std::allocator_traits::allocate(alloc, size); + // do placement new + return new (storage) + SkipListNode(uint8_t(height), std::forward(data), isHead); + } + + template + static void destroy(NodeAlloc& alloc, SkipListNode* node) { + size_t size = sizeof(SkipListNode) + + node->height_ * sizeof(std::atomic); + node->~SkipListNode(); + std::allocator_traits::deallocate(alloc, node, size); + } + + template + struct DestroyIsNoOp : StrictConjunction< + AllocatorHasTrivialDeallocate, + std::is_trivially_destructible> {}; + + // 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 acquireGuard() { + return std::unique_lock(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 + SkipListNode(uint8_t height, U&& data, bool isHead) + : height_(height), data_(std::forward(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(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 flags_; + const uint8_t height_; + MicroSpinLock spinLock_; + + value_type data_; + + std::atomic 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::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(sizeLimit); + } + lookupTable_[kMaxHeight - 1] = 1; + sizeLimitTable_[kMaxHeight - 1] = kMaxSizeLimit; + } + + static double randomProb() { + static ThreadLocal rng_; + return (*rng_)(); + } + + double lookupTable_[kMaxHeight]; + size_t sizeLimitTable_[kMaxHeight]; +}; + +template +class NodeRecycler; + +template +class NodeRecycler< + NodeType, + NodeAlloc, + typename std::enable_if< + !NodeType::template DestroyIsNoOp::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 g(lock_); + if (nodes_.get() == nullptr) { + nodes_ = std::make_unique>(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> newNodes; + { + std::lock_guard 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> nodes_; + std::atomic refs_; // current number of visitors to the list + std::atomic 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 +class NodeRecycler< + NodeType, + NodeAlloc, + typename std::enable_if< + NodeType::template DestroyIsNoOp::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 +// +// 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 SkipListT; + shared_ptr 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 +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace folly { + +template < + typename T, + typename Comp = std::less, + // All nodes are allocated using provided SysAllocator, + // it should be thread-safe. + typename NodeAlloc = SysAllocator, + 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 ScopedLocker; + typedef ConcurrentSkipList SkipListType; + + public: + typedef detail::SkipListNode NodeType; + typedef T value_type; + typedef T key_type; + + typedef detail::csl_iterator iterator; + typedef detail::csl_iterator 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 createInstance( + int height, + const NodeAlloc& alloc) { + return std::make_shared(height, alloc); + } + + static std::shared_ptr createInstance(int height = 1) { + return std::make_shared(height); + } + + //=================================================================== + // Below are implementation details. + // Please see ConcurrentSkipList::Accessor for stdlib-like APIs. + //=================================================================== + + ~ConcurrentSkipList() { + if /* constexpr */ (NodeType::template DestroyIsNoOp::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 + std::pair 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(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 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 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 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 recycler_; + std::atomic head_; + std::atomic size_; +}; + +template +class ConcurrentSkipList::Accessor { + typedef detail::SkipListNode NodeType; + typedef ConcurrentSkipList 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 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::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::value>::type> + std::pair insert(U&& data) { + auto ret = sl_->addOrGetData(std::forward(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 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 slHolder_; +}; + +// implements forward iterator concept. +template +class detail::csl_iterator : public detail::IteratorFacade< + csl_iterator, + 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 + csl_iterator( + const csl_iterator& other, + typename std::enable_if< + std::is_convertible::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 + friend class csl_iterator; + friend class detail:: + IteratorFacade; + + 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 +class ConcurrentSkipList::Skipper { + typedef detail::SkipListNode NodeType; + typedef ConcurrentSkipList 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& 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 +#include +#include +#include + +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 +constexpr T constexpr_max(T a) { + return a; +} +template +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 +constexpr T constexpr_min(T a) { + return a; +} +template +constexpr T constexpr_min(T a, T b, Ts... ts) { + return b < a ? constexpr_min(b, ts...) : constexpr_min(a, ts...); +} + +template +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 +constexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) { + return constexpr_clamp(v, lo, hi, std::less{}); +} + +namespace detail { + +template +struct constexpr_abs_helper {}; + +template +struct constexpr_abs_helper< + T, + typename std::enable_if::value>::type> { + static constexpr T go(T t) { + return t < static_cast(0) ? -t : t; + } +}; + +template +struct constexpr_abs_helper< + T, + typename std::enable_if< + std::is_integral::value && !std::is_same::value && + std::is_unsigned::value>::type> { + static constexpr T go(T t) { + return t; + } +}; + +template +struct constexpr_abs_helper< + T, + typename std::enable_if< + std::is_integral::value && !std::is_same::value && + std::is_signed::value>::type> { + static constexpr typename std::make_unsigned::type go(T t) { + return typename std::make_unsigned::type(t < static_cast(0) ? -t : t); + } +}; +} // namespace detail + +template +constexpr auto constexpr_abs(T t) + -> decltype(detail::constexpr_abs_helper::go(t)) { + return detail::constexpr_abs_helper::go(t); +} + +namespace detail { +template +constexpr T constexpr_log2_(T a, T e) { + return e == T(1) ? a : constexpr_log2_(a + T(1), e / T(2)); +} + +template +constexpr T constexpr_log2_ceil_(T l2, T t) { + return l2 + T(T(1) << l2 < t ? 1 : 0); +} + +template +constexpr T constexpr_square_(T t) { + return t * t; +} +} // namespace detail + +template +constexpr T constexpr_log2(T t) { + return detail::constexpr_log2_(T(0), t); +} + +template +constexpr T constexpr_log2_ceil(T t) { + return detail::constexpr_log2_ceil_(constexpr_log2(t), t); +} + +template +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 +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 +constexpr std::size_t constexpr_find_last_set(T const t) { + using U = std::make_unsigned_t; + return t == T(0) ? 0 : 1 + constexpr_log2(static_cast(t)); +} + +namespace detail { +template +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 +constexpr std::size_t constexpr_find_first_set(T t) { + using U = std::make_unsigned_t; + using size = std::integral_constant; + return t == T(0) + ? 0 + : 1 + detail::constexpr_find_first_set_(size{}, 0, static_cast(t)); +} + +template +constexpr T constexpr_add_overflow_clamped(T a, T b) { + using L = std::numeric_limits; + using M = std::intmax_t; + static_assert( + !std::is_integral::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::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 +constexpr T constexpr_sub_overflow_clamped(T a, T b) { + using L = std::numeric_limits; + using M = std::intmax_t; + static_assert( + !std::is_integral::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::value ? a - b : + // for unsigned type, keep result >= 0. + std::is_unsigned::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(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(NaN) = 6 +// clamp_cast(NaN) = 0 +// +// static_cast(9999999999.0f) = -348639895 +// clamp_cast(9999999999.0f) = 2147483647 +// +// static_cast(2147483647.0f) = -348639895 +// clamp_cast(2147483647.0f) = 2147483647 +// +// static_cast(4294967295.0f) = 0 +// clamp_cast(4294967295.0f) = 4294967295 +// +// static_cast(-1) = 4294967295 +// clamp_cast(-1) = 0 +// +// static_cast(32768u) = -32768 +// clamp_cast(32768u) = 32767 + +template +constexpr typename std::enable_if::value, Dst>::type +constexpr_clamp_cast(Src src) { + static_assert( + std::is_integral::value && sizeof(Dst) <= sizeof(int64_t), + "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); + + using L = std::numeric_limits; + // clang-format off + return + // Check if Src and Dst have same signedness. + std::is_signed::value == std::is_signed::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::value && std::is_unsigned::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 +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 +constexpr typename std::enable_if::value, Dst>::type +constexpr_clamp_cast(Src src) { + static_assert( + std::is_integral::value && sizeof(Dst) <= sizeof(int64_t), + "constexpr_clamp_cast can only cast into integral type (up to 64bit)"); + + using L = std::numeric_limits; + // 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( + constexpr_clamp_cast(double(src))) + : constexpr_clamp_cast( + constexpr_clamp_cast(double(src)))) : + // The following are for sizeof(Src) == sizeof(Dst). + std::is_same::value && std::is_same::value ? + detail::constexpr_clamp_cast_helper( + double(src), + detail::kClampCastLowerBoundDoubleToInt64F, + detail::kClampCastUpperBoundDoubleToInt64F, + L::min(), + L::max()) : + std::is_same::value && std::is_same::value ? + detail::constexpr_clamp_cast_helper( + double(src), + 0.0, + detail::kClampCastUpperBoundDoubleToUInt64F, + L::min(), + L::max()) : + std::is_same::value && std::is_same::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 +#include + +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(*b) - '0'; + if (c >= 10) { + break; + } + } + return b; +} + +// Maximum value of number when represented as a string +template +struct MaxString { + static const char* const value; +}; + +template <> +const char* const MaxString::value = "255"; +template <> +const char* const MaxString::value = "65535"; +template <> +const char* const MaxString::value = "4294967295"; +#if __SIZEOF_LONG__ == 4 +template <> +const char* const MaxString::value = "4294967295"; +#else +template <> +const char* const MaxString::value = "18446744073709551615"; +#endif +static_assert( + sizeof(unsigned long) >= 4, + "Wrong value for MaxString::value," + " please update."); +template <> +const char* const MaxString::value = "18446744073709551615"; +static_assert( + sizeof(unsigned long long) >= 8, + "Wrong value for MaxString::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(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 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 +Expected 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::quiet_NaN(), + nullptr, + nullptr); + + if (src->empty()) { + return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); + } + + int length; + auto result = conv.StringToDouble( + src->data(), + static_cast(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::infinity(); + } + break; + + case 'n': + if (size >= 3 && tolower_ascii(b[1]) == 'a' && + tolower_ascii(b[2]) == 'n') { + b += 3; + result = std::numeric_limits::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 str_to_floating( + StringPiece* src) noexcept; +template Expected str_to_floating( + StringPiece* src) noexcept; + +/** + * This class takes care of additional processing needed for signed values, + * like leading sign character and overflow checks. + */ +template ::value> +class SignedValueHandler; + +template +class SignedValueHandler { + 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 + Expected 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 +class SignedValueHandler { + public: + ConversionCode init(const char*&) { + return ConversionCode::SUCCESS; + } + + ConversionCode overflow() { + return ConversionCode::POSITIVE_OVERFLOW; + } + + Expected 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 +inline Expected digits_to( + const char* b, + const char* const e) noexcept { + using UT = typename std::make_unsigned::type; + assert(b <= e); + + SignedValueHandler 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::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::digits10 && + (size != std::numeric_limits::digits10 + 1 || + strncmp(b, MaxString::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(b[0])]; + const int32_t r1 = shift100[static_cast(b[1])]; + const int32_t r2 = shift10[static_cast(b[2])]; + const int32_t r3 = shift1[static_cast(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(b[0])]; + const int32_t r1 = shift10[static_cast(b[1])]; + const int32_t r2 = shift1[static_cast(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(b[0])]; + const int32_t r1 = shift1[static_cast(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(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 digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected digits_to( + const char*, + const char*) noexcept; + +template Expected digits_to( + const char*, + const char*) noexcept; +template Expected +digits_to(const char*, const char*) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> digits_to<__int128>( + const char*, + const char*) noexcept; +template Expected +digits_to(const char*, const char*) noexcept; +#endif + +/** + * StringPiece to integrals, with progress information. Alters the + * StringPiece parameter to munch the already-parsed characters. + */ +template +Expected str_to_integral(StringPiece* src) noexcept { + using UT = typename std::make_unsigned::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 sgn; + auto err = sgn.init(b); + + if (UNLIKELY(err != ConversionCode::SUCCESS)) { + return makeUnexpected(err); + } + if (std::is_signed::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(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 str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected str_to_integral( + StringPiece* src) noexcept; + +template Expected str_to_integral( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; + +#if FOLLY_HAVE_INT128_T +template Expected<__int128, ConversionCode> str_to_integral<__int128>( + StringPiece* src) noexcept; +template Expected +str_to_integral(StringPiece* src) noexcept; +#endif + +} // namespace detail + +ConversionError makeConversionError(ConversionCode code, StringPiece input) { + using namespace detail; + static_assert( + std::is_unsigned::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 or to. These are variadic + * functions that convert their arguments to strings, and concatenate them to + * form a result. So, for example, + * + * auto str = to(123, "456", 789); + * + * Sets str to "123456789". + * + * In addition to just concatenating the arguments, related functions can + * delimit them with some string: toDelim(",", "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("123"); // Returns 123. + * + * Out of range (e.g. to("1000")), or invalidly formatted (e.g. + * to("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, which will return an Expected. + * + * 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(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 parseTo(folly::StringPiece in, + * YourType& out); + * YourErrorType makeConversionError(YourErrorType in, StringPiece in); + * // Two functions to allow conversion from your type to a string. + * template + * void toAppend(const YourType& in, String* out); + * size_t estimateSpaceNeeded(const YourType& in); + * + * These are documented below, inline. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // V8 JavaScript implementation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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) returns itself for all types T. + */ +template +typename std::enable_if< + std::is_same::type>::value, + Expected>::type +tryTo(Src&& value) { + return std::forward(value); +} + +template +typename std::enable_if< + std::is_same::type>::value, + Tgt>::type +to(Src&& value) { + return std::forward(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 +typename std::enable_if< + std::is_arithmetic::value && !std::is_same::value && + std::is_same::value, + Expected>::type +tryTo(const Src& value) { + return value != Src(); +} + +template +typename std::enable_if< + std::is_arithmetic::value && !std::is_same::value && + std::is_same::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 +auto getLastElement(Ts&&... ts) -> decltype(std::get( + std::forward_as_tuple(std::forward(ts)...))) { + return std::get( + std::forward_as_tuple(std::forward(ts)...)); +} + +inline void getLastElement() {} + +template +struct LastElementType : std::tuple_element> {}; + +template <> +struct LastElementType<0> { + using type = void; +}; + +template +struct LastElement + : std::decay::type> {}; +#else +template +struct LastElementImpl { + static void call(Ignored...) {} +}; + +template +struct LastElementImpl { + template + static Last call(Ignored..., Last&& last) { + return std::forward(last); + } +}; + +template +auto getLastElement(const Ts&... ts) + -> decltype(LastElementImpl::call(ts...)) { + return LastElementImpl::call(ts...); +} + +template +struct LastElement : std::decay::call(std::declval()...))> { +}; +#endif + +} // namespace detail + +/******************************************************************************* + * Conversions from integral types to string types. + ******************************************************************************/ + +#if FOLLY_HAVE_INT128_T +namespace detail { + +template +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::digits10; + return static_cast(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('0' + digit); + x = y; + } + + uint64_t xx = static_cast(x); // Rest uses faster 64-bit division + + while (xx >= 10) { + const auto y = xx / 10ULL; + const auto digit = xx % 10ULL; + + buffer[p--] = static_cast('0' + digit); + xx = y; + } + + buffer[p] = static_cast('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('0' + r); + v = q; + } + // Last digit is trivial to handle + buffer[pos] = static_cast(v + '0'); + return result; +} + +/** + * A single char gets appended. + */ +template +void toAppend(char value, Tgt* result) { + *result += value; +} + +template +constexpr typename std::enable_if::value, size_t>::type +estimateSpaceNeeded(T) { + return 1; +} + +template +constexpr size_t estimateSpaceNeeded(const char (&)[N]) { + return N; +} + +/** + * Everything implicitly convertible to const char* gets appended. + */ +template +typename std::enable_if< + std::is_convertible::value && + IsSomeString::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 +typename std::enable_if::value, size_t>:: + type + estimateSpaceNeeded(Src value) { + const char* c = value; + if (c) { + return folly::StringPiece(value).size(); + }; + return 0; +} + +template +typename std::enable_if::value, size_t>::type +estimateSpaceNeeded(Src const& value) { + return value.size(); +} + +template +typename std::enable_if< + std::is_convertible::value && + !IsSomeString::value && + !std::is_convertible::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return folly::StringPiece(value).size(); +} + +template <> +inline size_t estimateSpaceNeeded(std::nullptr_t /* value */) { + return 0; +} + +template +typename std::enable_if< + std::is_pointer::value && + IsSomeString>::value, + size_t>::type +estimateSpaceNeeded(Src value) { + return value->size(); +} + +/** + * Strings get appended, too. + */ +template +typename std::enable_if< + IsSomeString::value && IsSomeString::value>::type +toAppend(const Src& value, Tgt* result) { + result->append(value); +} + +/** + * and StringPiece objects too + */ +template +typename std::enable_if::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 +typename std::enable_if::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 +void toAppend(__int128 value, Tgt* result) { + typedef unsigned __int128 Usrc; + char buffer[detail::digitsEnough() + 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 +void toAppend(unsigned __int128 value, Tgt* result) { + char buffer[detail::digitsEnough()]; + size_t p; + + p = detail::unsafeTelescope128(buffer, sizeof(buffer), value); + + result->append(buffer + p, buffer + sizeof(buffer)); +} + +template +constexpr + typename std::enable_if::value, size_t>::type + estimateSpaceNeeded(T) { + return detail::digitsEnough<__int128>(); +} + +template +constexpr typename std:: + enable_if::value, size_t>::type + estimateSpaceNeeded(T) { + return detail::digitsEnough(); +} + +#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 +typename std::enable_if< + std::is_integral::value && std::is_signed::value && + IsSomeString::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(value) + 1, buffer)); + } else { + result->append(buffer, uint64ToBufferUnsafe(uint64_t(value), buffer)); + } +} + +template +typename std::enable_if< + std::is_integral::value && std::is_signed::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(value) + 1" + return 1 + digits10(~static_cast(value) + 1); + } + + return digits10(static_cast(value)); +} + +/** + * As above, but for uint32_t and uint64_t. + */ +template +typename std::enable_if< + std::is_integral::value && !std::is_signed::value && + IsSomeString::value && sizeof(Src) >= 4>::type +toAppend(Src value, Tgt* result) { + char buffer[20]; + result->append(buffer, uint64ToBufferUnsafe(value, buffer)); +} + +template +typename std::enable_if< + std::is_integral::value && !std::is_signed::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 +typename std::enable_if< + std::is_integral::value && IsSomeString::value && + sizeof(Src) < 4>::type +toAppend(Src value, Tgt* result) { + typedef + typename std::conditional::value, int64_t, uint64_t>:: + type Intermediate; + toAppend(static_cast(value), result); +} + +template +typename std::enable_if< + std::is_integral::value && sizeof(Src) < 4 && + !std::is_same::value, + size_t>::type +estimateSpaceNeeded(Src value) { + typedef + typename std::conditional::value, int64_t, uint64_t>:: + type Intermediate; + return estimateSpaceNeeded(static_cast(value)); +} + +/** + * Enumerated values get appended as integers. + */ +template +typename std::enable_if< + std::is_enum::value && IsSomeString::value>::type +toAppend(Src value, Tgt* result) { + toAppend(to_underlying(value), result); +} + +template +typename std::enable_if::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 +typename std::enable_if< + std::is_floating_point::value && IsSomeString::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(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 +typename std::enable_if< + std::is_floating_point::value && IsSomeString::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 +typename std::enable_if::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::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 +struct HasLengthEstimator : std::false_type {}; + +template +constexpr typename std::enable_if< + !std::is_fundamental::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::value && +#endif + !IsSomeString::value && + !std::is_convertible::value && + !std::is_convertible::value && + !std::is_enum::value && !HasLengthEstimator::value, + size_t>::type +estimateSpaceNeeded(const Src&) { + return sizeof(Src) + 1; // dumbest best effort ever? +} + +namespace detail { + +template +typename std::enable_if::value, size_t>::type +estimateSpaceToReserve(size_t sofar, Tgt*) { + return sofar; +} + +template +size_t estimateSpaceToReserve(size_t sofar, const T& v, const Ts&... vs) { + return estimateSpaceToReserve(sofar + estimateSpaceNeeded(v), vs...); +} + +template +void reserveInTarget(const Ts&... vs) { + getLastElement(vs...)->reserve(estimateSpaceToReserve(0, vs...)); +} + +template +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(nullptr)); + getLastElement(vs...)->reserve(estimateSpaceToReserve(fordelim, vs...)); +} + +/** + * Variadic base case: append one element + */ +template +typename std::enable_if< + IsSomeString::type>::value>::type +toAppendStrImpl(const T& v, Tgt result) { + toAppend(v, result); +} + +template +typename std::enable_if< + sizeof...(Ts) >= 2 && + IsSomeString::type>::type>::value>::type +toAppendStrImpl(const T& v, const Ts&... vs) { + toAppend(v, getLastElement(vs...)); + toAppendStrImpl(vs...); +} + +template +typename std::enable_if< + IsSomeString::type>::value>::type +toAppendDelimStrImpl(const Delimiter& /* delim */, const T& v, Tgt result) { + toAppend(v, result); +} + +template +typename std::enable_if< + sizeof...(Ts) >= 2 && + IsSomeString::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 + * void toAppend(const OtherType&, String* out); + * + * // optional + * size_t estimateSpaceNeeded(const OtherType&); + * + * } + */ +template +typename std::enable_if< + sizeof...(Ts) >= 3 && + IsSomeString::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 +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 +typename std::enable_if::type>::type>::value>::type +toAppendFit(const Ts&... vs) { + ::folly::detail::reserveInTarget(vs...); + toAppend(vs...); +} + +template +void toAppendFit(const Ts&) {} + +/** + * Variadic base case: do nothing. + */ +template +typename std::enable_if::value>::type toAppend( + Tgt* /* result */) {} + +/** + * Variadic base case: do nothing. + */ +template +typename std::enable_if::value>::type toAppendDelim( + const Delimiter& /* delim */, + Tgt* /* result */) {} + +/** + * 1 element: same as toAppend. + */ +template +typename std::enable_if::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 +typename std::enable_if< + sizeof...(Ts) >= 3 && + IsSomeString::type>::type>::value>::type +toAppendDelim(const Delimiter& delim, const Ts&... vs) { + detail::toAppendDelimStrImpl(delim, vs...); +} + +/** + * Detail in comment for toAppendFit + */ +template +typename std::enable_if::type>::type>::value>::type +toAppendDelimFit(const Delimiter& delim, const Ts&... vs) { + detail::reserveInTargetDelim(delim, vs...); + toAppendDelim(delim, vs...); +} + +template +void toAppendDelimFit(const De&, const Ts&) {} + +/** + * to(v1, v2, ...) uses toAppend() (see below) as back-end + * for all types. + */ +template +typename std::enable_if< + IsSomeString::value && + (sizeof...(Ts) != 1 || + !std::is_same::type>:: + value), + Tgt>::type +to(const Ts&... vs) { + Tgt result; + toAppendFit(vs..., &result); + return result; +} + +/** + * Special version of to for floating point. When calling + * folly::to(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 +typename std::enable_if< + IsSomeString::value && std::is_floating_point::value, + Tgt>::type +to(Src value) { + Tgt result; + toAppend(value, &result); + return result; +} + +/** + * toDelim(SomeString str) returns itself. + */ +template +typename std::enable_if< + IsSomeString::value && + std::is_same::type>::value, + Tgt>::type +toDelim(const Delim& /* delim */, Src&& value) { + return std::forward(value); +} + +/** + * toDelim(delim, v1, v2, ...) uses toAppendDelim() as + * back-end for all types. + */ +template +typename std::enable_if< + IsSomeString::value && + (sizeof...(Ts) != 1 || + !std::is_same::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 str_to_bool(StringPiece* src) noexcept; + +template +Expected str_to_floating(StringPiece* src) noexcept; + +extern template Expected str_to_floating( + StringPiece* src) noexcept; +extern template Expected str_to_floating( + StringPiece* src) noexcept; + +template +Expected digits_to(const char* b, const char* e) noexcept; + +extern template Expected digits_to( + const char*, + const char*) noexcept; +extern template Expected digits_to( + const char*, + const char*) noexcept; +extern template Expected +digits_to(const char*, const char*) noexcept; + +extern template Expected digits_to( + const char*, + const char*) noexcept; +extern template Expected +digits_to(const char*, const char*) noexcept; + +extern template Expected digits_to( + const char*, + const char*) noexcept; +extern template Expected digits_to( + const char*, + const char*) noexcept; + +extern template Expected digits_to( + const char*, + const char*) noexcept; +extern template Expected +digits_to(const char*, const char*) noexcept; + +extern template Expected digits_to( + const char*, + const char*) noexcept; +extern template Expected +digits_to(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 +digits_to(const char*, const char*) noexcept; +#endif + +template +Expected str_to_integral(StringPiece* src) noexcept; + +extern template Expected str_to_integral( + StringPiece* src) noexcept; +extern template Expected +str_to_integral(StringPiece* src) noexcept; +extern template Expected +str_to_integral(StringPiece* src) noexcept; + +extern template Expected str_to_integral( + StringPiece* src) noexcept; +extern template Expected +str_to_integral(StringPiece* src) noexcept; + +extern template Expected str_to_integral( + StringPiece* src) noexcept; +extern template Expected +str_to_integral(StringPiece* src) noexcept; + +extern template Expected str_to_integral( + StringPiece* src) noexcept; +extern template Expected +str_to_integral(StringPiece* src) noexcept; + +extern template Expected str_to_integral( + StringPiece* src) noexcept; +extern template Expected +str_to_integral(StringPiece* src) noexcept; + +#if FOLLY_HAVE_INT128_T +extern template Expected<__int128, ConversionCode> str_to_integral<__int128>( + StringPiece* src) noexcept; +extern template Expected +str_to_integral(StringPiece* src) noexcept; +#endif + +template +typename std:: + enable_if::value, Expected>::type + convertTo(StringPiece* src) noexcept { + return str_to_bool(src); +} + +template +typename std::enable_if< + std::is_floating_point::value, + Expected>::type +convertTo(StringPiece* src) noexcept { + return str_to_floating(src); +} + +template +typename std::enable_if< + std::is_integral::value && !std::is_same::value, + Expected>::type +convertTo(StringPiece* src) noexcept { + return str_to_integral(src); +} + +} // namespace detail + +/** + * String represented as a pair of pointers to char to unsigned + * integrals. Assumes NO whitespace before or after. + */ +template +typename std::enable_if< + std::is_integral::value && !std::is_same::value, + Expected>::type +tryTo(const char* b, const char* e) { + return detail::digits_to(b, e); +} + +template +typename std::enable_if< + std::is_integral::value && !std::is_same::value, + Tgt>::type +to(const char* b, const char* e) { + return tryTo(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 +FOLLY_NODISCARD inline typename std::enable_if< + std::is_arithmetic::value, + Expected>::type +parseTo(StringPiece src, Tgt& out) { + return detail::convertTo(&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 +typename std::enable_if< + !std::is_same::value && + (std::is_integral::value || std::is_floating_point::value), + Expected>::type +convertTo(const bool& value) noexcept { + return static_cast(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 +typename std::enable_if< + std::is_integral::value && !std::is_same::value && + !std::is_same::value && std::is_integral::value, + Expected>::type +convertTo(const Src& value) noexcept { + if /* constexpr */ ( + std::make_unsigned_t(std::numeric_limits::max()) < + std::make_unsigned_t(std::numeric_limits::max())) { + if (greater_than::max()>(value)) { + return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW); + } + } + if /* constexpr */ ( + std::is_signed::value && + (!std::is_signed::value || sizeof(Src) > sizeof(Tgt))) { + if (less_than::min()>(value)) { + return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW); + } + } + return static_cast(value); +} + +/** + * Checked conversion from floating to floating. The checks are only + * performed when meaningful, e.g. conversion from float to double goes + * unchecked. + */ +template +typename std::enable_if< + std::is_floating_point::value && std::is_floating_point::value && + !std::is_same::value, + Expected>::type +convertTo(const Src& value) noexcept { + if /* constexpr */ ( + std::numeric_limits::max() < std::numeric_limits::max()) { + if (value > std::numeric_limits::max()) { + return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW); + } + if (value < std::numeric_limits::lowest()) { + return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW); + } + } + return static_cast(value); +} + +/** + * Check if a floating point value can safely be converted to an + * integer value without triggering undefined behaviour. + */ +template +inline typename std::enable_if< + std::is_floating_point::value && std::is_integral::value && + !std::is_same::value, + bool>::type +checkConversion(const Src& value) { + constexpr Src tgtMaxAsSrc = static_cast(std::numeric_limits::max()); + constexpr Src tgtMinAsSrc = static_cast(std::numeric_limits::min()); + if (value >= tgtMaxAsSrc) { + if (value > tgtMaxAsSrc) { + return false; + } + const Src mmax = folly::nextafter(tgtMaxAsSrc, Src()); + if (static_cast(value - mmax) > + std::numeric_limits::max() - static_cast(mmax)) { + return false; + } + } else if (std::is_signed::value && value <= tgtMinAsSrc) { + if (value < tgtMinAsSrc) { + return false; + } + const Src mmin = folly::nextafter(tgtMinAsSrc, Src()); + if (static_cast(value - mmin) < + std::numeric_limits::min() - static_cast(mmin)) { + return false; + } + } + return true; +} + +// Integers can always safely be converted to floating point values +template +constexpr typename std::enable_if< + std::is_integral::value && std::is_floating_point::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 +constexpr typename std::enable_if< + std::is_floating_point::value && std::is_same::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 . + */ +template +typename std::enable_if< + (std::is_integral::value && std::is_floating_point::value) || + (std::is_floating_point::value && std::is_integral::value), + Expected>::type +convertTo(const Src& value) noexcept { + if (LIKELY(checkConversion(value))) { + Tgt result = static_cast(value); + if (LIKELY(checkConversion(result))) { + Src witness = static_cast(result); + if (LIKELY(value == witness)) { + return result; + } + } + } + return makeUnexpected(ConversionCode::ARITH_LOSS_OF_PRECISION); +} + +template +inline std::string errorValue(const Src& value) { + return to("(", pretty_name(), ") ", value); +} + +template +using IsArithToArith = bool_constant< + !std::is_same::value && !std::is_same::value && + std::is_arithmetic::value && std::is_arithmetic::value>; + +} // namespace detail + +template +typename std::enable_if< + detail::IsArithToArith::value, + Expected>::type +tryTo(const Src& value) noexcept { + return detail::convertTo(value); +} + +template +typename std::enable_if::value, Tgt>::type to( + const Src& value) { + return tryTo(value).thenOrThrow( + [](Tgt res) { return res; }, + [&](ConversionCode e) { + return makeConversionError(e, detail::errorValue(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 +FOLLY_NODISCARD typename std::enable_if< + std::is_enum::value, + Expected>::type +parseTo(StringPiece in, T& out) noexcept { + typename std::underlying_type::type tmp{}; + auto restOrError = parseTo(in, tmp); + out = static_cast(tmp); // Harmless if parseTo fails + return restOrError; +} + +FOLLY_NODISCARD +inline Expected parseTo( + StringPiece in, + StringPiece& out) noexcept { + out = in; + return StringPiece{in.end(), in.end()}; +} + +FOLLY_NODISCARD +inline Expected 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 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 +using ParseToResult = decltype(parseTo(StringPiece{}, std::declval())); + +struct CheckTrailingSpace { + Expected operator()(StringPiece sp) const { + auto e = enforceWhitespaceErr(sp); + if (UNLIKELY(e != ConversionCode::SUCCESS)) { + return makeUnexpected(e); + } + return unit; + } +}; + +template +struct ReturnUnit { + template + constexpr Expected operator()(T&&) const { + return unit; + } +}; + +// Older versions of the parseTo customization point threw on error and +// returned void. Handle that. +template +inline typename std::enable_if< + std::is_void>::value, + Expected>::type +parseToWrap(StringPiece sp, Tgt& out) { + parseTo(sp, out); + return StringPiece(sp.end(), sp.end()); +} + +template +inline typename std::enable_if< + !std::is_void>::value, + ParseToResult>::type +parseToWrap(StringPiece sp, Tgt& out) { + return parseTo(sp, out); +} + +template +using ParseToError = ExpectedErrorType()))>; + +} // namespace detail + +/** + * String or StringPiece to target conversion. Accepts leading and trailing + * whitespace, but no non-space trailing characters. + */ + +template +inline typename std::enable_if< + !std::is_same::value, + Expected>>::type +tryTo(StringPiece src) { + Tgt result{}; + using Error = detail::ParseToError; + using Check = typename std::conditional< + std::is_arithmetic::value, + detail::CheckTrailingSpace, + detail::ReturnUnit>::type; + return parseTo(src, result).then(Check(), [&](Unit) { + return std::move(result); + }); +} + +template +inline typename std::enable_if< + IsSomeString::value && !std::is_same::value, + Tgt>::type +to(Src const& src) { + return to(StringPiece(src.data(), src.size())); +} + +template +inline + typename std::enable_if::value, Tgt>::type + to(StringPiece src) { + Tgt result{}; + using Error = detail::ParseToError; + using Check = typename std::conditional< + std::is_arithmetic::value, + detail::CheckTrailingSpace, + detail::ReturnUnit>::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 +Expected> tryTo(StringPiece* src) { + Tgt result; + return parseTo(*src, result).then([&, src](StringPiece sp) -> Tgt { + *src = sp; + return std::move(result); + }); +} + +template +Tgt to(StringPiece* src) { + Tgt result{}; + using Error = detail::ParseToError; + 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 +typename std::enable_if< + std::is_enum::value && !std::is_same::value && + !std::is_convertible::value, + Expected>::type +tryTo(const Src& value) { + return tryTo(to_underlying(value)); +} + +template +typename std::enable_if< + !std::is_convertible::value && std::is_enum::value && + !std::is_same::value, + Expected>::type +tryTo(const Src& value) { + using I = typename std::underlying_type::type; + return tryTo(value).then([](I i) { return static_cast(i); }); +} + +template +typename std::enable_if< + std::is_enum::value && !std::is_same::value && + !std::is_convertible::value, + Tgt>::type +to(const Src& value) { + return to(to_underlying(value)); +} + +template +typename std::enable_if< + !std::is_convertible::value && std::is_enum::value && + !std::is_same::value, + Tgt>::type +to(const Src& value) { + return static_cast(to::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 + +#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 + +#include + +#ifdef _MSC_VER +#include +#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(reg), 0); + const int n = reg[0]; + if (n >= 1) { + __cpuid(static_cast(reg), 1); + f1c_ = uint32_t(reg[2]); + f1d_ = uint32_t(reg[3]); + } + if (n >= 7) { + __cpuidex(static_cast(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 + +#include + +#include +#include + +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 keepAliveCount_{1}; + }; + + class WeakRef : public Executor { + public: + static folly::Executor::KeepAlive<> create( + std::shared_ptr 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, 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 keepAliveCount_{1}; + + std::shared_ptr 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_{std::make_shared()}; + Baton<> keepAliveReleaseBaton_; + KeepAlive 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 + +#include +#include + +#include +#include +#include + +#if FOLLY_DETAIL_HAVE_DEMANGLE_H + +#include + +#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(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 + +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 +#include + +#include + +#include +#include +#include + +#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 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 +class DiscriminatedPtr { + // <, not <=, as our indexes are 1-based (0 means "empty") + static_assert( + sizeof...(Types) < std::numeric_limits::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 + explicit DiscriminatedPtr(T* ptr) { + set(ptr, typeIndex()); + } + + /** + * 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 + void set(T* ptr) { + set(ptr, typeIndex()); + } + + /** + * 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 + T* get_nothrow() noexcept { + void* p = LIKELY(hasType()) ? ptr() : nullptr; + return static_cast(p); + } + + template + const T* get_nothrow() const noexcept { + const void* p = LIKELY(hasType()) ? ptr() : nullptr; + return static_cast(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 + T* get() { + if (UNLIKELY(!hasType())) { + throw std::invalid_argument("Invalid type"); + } + return static_cast(ptr()); + } + + template + const T* get() const { + if (UNLIKELY(!hasType())) { + throw std::invalid_argument("Invalid type"); + } + return static_cast(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 + bool hasType() const { + return index() == typeIndex(); + } + + /** + * Clear this DiscriminatedPtr, making it empty. + */ + void clear() { + data_ = 0; + } + + /** + * Assignment operator from a pointer of type T. + */ + template + 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 dptr_detail::VisitorResult::type apply(V&& visitor) { + size_t n = index(); + if (n == 0) { + throw std::invalid_argument("Empty DiscriminatedPtr"); + } + return dptr_detail::ApplyVisitor()( + n, std::forward(visitor), ptr()); + } + + template + typename dptr_detail::ConstVisitorResult::type apply( + V&& visitor) const { + size_t n = index(); + if (n == 0) { + throw std::invalid_argument("Empty DiscriminatedPtr"); + } + return dptr_detail::ApplyConstVisitor()( + n, std::forward(visitor), ptr()); + } + + private: + /** + * Get the 1-based type index of T in Types. + */ + template + uint16_t typeIndex() const { + return uint16_t(dptr_detail::GetTypeIndex::value); + } + + uint16_t index() const { + return data_ >> 48; + } + void* ptr() const { + return reinterpret_cast(data_ & ((1ULL << 48) - 1)); + } + + void set(void* p, uint16_t v) { + uintptr_t ip = reinterpret_cast(p); + CHECK(!(ip >> 48)); + ip |= static_cast(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 +decltype(auto) apply_visitor( + Visitor&& visitor, + const DiscriminatedPtr& variant) { + return variant.apply(std::forward(visitor)); +} + +template +decltype(auto) apply_visitor( + Visitor&& visitor, + DiscriminatedPtr& variant) { + return variant.apply(std::forward(visitor)); +} + +template +decltype(auto) apply_visitor( + Visitor&& visitor, + DiscriminatedPtr&& variant) { + return variant.apply(std::forward(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 + +#pragma once + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace folly { +template +T convertTo(const dynamic&); +template +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>>(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 +struct iterator_class_is_container { + typedef std::reverse_iterator some_iterator; + enum { + value = has_value_type::value && + std::is_constructible::value + }; +}; + +template +using class_is_container = + Conjunction, iterator_class_is_container>; + +template +using is_range = StrictConjunction, has_iterator>; + +template +using is_container = StrictConjunction, class_is_container>; + +template +using is_map = StrictConjunction, has_mapped_type>; + +template +using is_associative = StrictConjunction, has_key_type>; + +} // 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 +struct Dereferencer { + static inline void derefToCache( + Optional* /* mem */, + const dynamic::const_item_iterator& /* it */) { + throw_exception("array", dynamic::Type::OBJECT); + } + + static inline void derefToCache( + Optional* mem, + const dynamic::const_iterator& it) { + mem->emplace(convertTo(*it)); + } +}; + +template +struct Dereferencer> { + static inline void derefToCache( + Optional>* mem, + const dynamic::const_item_iterator& it) { + mem->emplace(convertTo(it->first), convertTo(it->second)); + } + + // Intentional duplication of the code in Dereferencer + template + static inline void derefToCache( + Optional* mem, + const dynamic::const_iterator& it) { + mem->emplace(convertTo(*it)); + } +}; + +template +class Transformer + : public boost:: + iterator_adaptor, It, typename T::value_type> { + friend class boost::iterator_core_access; + + typedef typename T::value_type ttype; + + mutable Optional cache_; + + void increment() { + ++this->base_reference(); + cache_ = none; + } + + ttype& dereference() const { + if (!cache_) { + Dereferencer::derefToCache(&cache_, this->base_reference()); + } + return cache_.value(); + } + + public: + explicit Transformer(const It& it) : Transformer::iterator_adaptor_(it) {} +}; + +// conversion factory +template +inline std::move_iterator> conversionIterator(const It& it) { + return std::make_move_iterator(Transformer(it)); +} + +} // namespace dynamicconverter_detail + +/////////////////////////////////////////////////////////////////////////////// +// DynamicConverter specializations + +/** + * Each specialization of DynamicConverter has the function + * 'static T convert(const dynamic&);' + */ + +// default - intentionally unimplemented +template +struct DynamicConverter; + +// boolean +template <> +struct DynamicConverter { + static bool convert(const dynamic& d) { + return d.asBool(); + } +}; + +// integrals +template +struct DynamicConverter< + T, + typename std::enable_if< + std::is_integral::value && !std::is_same::value>::type> { + static T convert(const dynamic& d) { + return folly::to(d.asInt()); + } +}; + +// enums +template +struct DynamicConverter< + T, + typename std::enable_if::value>::type> { + static T convert(const dynamic& d) { + using type = typename std::underlying_type::type; + return static_cast(DynamicConverter::convert(d)); + } +}; + +// floating point +template +struct DynamicConverter< + T, + typename std::enable_if::value>::type> { + static T convert(const dynamic& d) { + return folly::to(d.asDouble()); + } +}; + +// fbstring +template <> +struct DynamicConverter { + static folly::fbstring convert(const dynamic& d) { + return d.asString(); + } +}; + +// std::string +template <> +struct DynamicConverter { + static std::string convert(const dynamic& d) { + return d.asString(); + } +}; + +// std::pair +template +struct DynamicConverter> { + static std::pair convert(const dynamic& d) { + if (d.isArray() && d.size() == 2) { + return std::make_pair(convertTo(d[0]), convertTo(d[1])); + } else if (d.isObject() && d.size() == 1) { + auto it = d.items().begin(); + return std::make_pair(convertTo(it->first), convertTo(it->second)); + } else { + throw_exception("array (size 2) or object (size 1)", d.type()); + } + } +}; + +// non-associative containers +template +struct DynamicConverter< + C, + typename std::enable_if< + dynamicconverter_detail::is_container::value && + !dynamicconverter_detail::is_associative::value>::type> { + static C convert(const dynamic& d) { + if (d.isArray()) { + return C( + dynamicconverter_detail::conversionIterator(d.begin()), + dynamicconverter_detail::conversionIterator(d.end())); + } else if (d.isObject()) { + return C( + dynamicconverter_detail::conversionIterator(d.items().begin()), + dynamicconverter_detail::conversionIterator(d.items().end())); + } else { + throw_exception("object or array", d.type()); + } + } +}; + +// associative containers +template +struct DynamicConverter< + C, + typename std::enable_if< + dynamicconverter_detail::is_container::value && + dynamicconverter_detail::is_associative::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(d.begin()), + dynamicconverter_detail::conversionIterator(d.end())); + } else if (d.isObject()) { + ret.insert( + dynamicconverter_detail::conversionIterator(d.items().begin()), + dynamicconverter_detail::conversionIterator(d.items().end())); + } else { + throw_exception("object or array", d.type()); + } + return ret; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// DynamicConstructor specializations + +/** + * Each specialization of DynamicConstructor has the function + * 'static dynamic construct(const C&);' + */ + +// default +template +struct DynamicConstructor { + static dynamic construct(const C& x) { + return dynamic(x); + } +}; + +// identity +template +struct DynamicConstructor< + C, + typename std::enable_if::value>::type> { + static dynamic construct(const C& x) { + return x; + } +}; + +// enums +template +struct DynamicConstructor< + C, + typename std::enable_if::value>::type> { + static dynamic construct(const C& x) { + return dynamic(to_underlying(x)); + } +}; + +// maps +template +struct DynamicConstructor< + C, + typename std::enable_if< + !std::is_same::value && + dynamicconverter_detail::is_map::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 +struct DynamicConstructor< + C, + typename std::enable_if< + !std::is_same::value && + !dynamicconverter_detail::is_map::value && + !std::is_constructible::value && + dynamicconverter_detail::is_range::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 +struct DynamicConstructor, void> { + static dynamic construct(const std::pair& x) { + dynamic d = dynamic::array; + d.push_back(toDynamic(x.first)); + d.push_back(toDynamic(x.second)); + return d; + } +}; + +// vector +template <> +struct DynamicConstructor, void> { + static dynamic construct(const std::vector& x) { + dynamic d = dynamic::array; + // Intentionally specifying the type as bool here. + // std::vector'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 +T convertTo(const dynamic& d) { + return DynamicConverter::type>::convert(d); +} + +template +dynamic toDynamic(const T& x) { + return DynamicConstructor::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 + +#include +#include +#include + +#include +#include +#include +#include + +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 +std::system_error makeSystemErrorExplicit(int err, Args&&... args) { + return makeSystemErrorExplicit( + err, to(std::forward(args)...).c_str()); +} + +inline std::system_error makeSystemError(const char* msg) { + return makeSystemErrorExplicit(errno, msg); +} + +template +std::system_error makeSystemError(Args&&... args) { + return makeSystemErrorExplicit(errno, std::forward(args)...); +} + +// Helper to throw std::system_error +[[noreturn]] inline void throwSystemErrorExplicit(int err, const char* msg) { + throw_exception(makeSystemErrorExplicit(err, msg)); +} + +template +[[noreturn]] void throwSystemErrorExplicit(int err, Args&&... args) { + throw_exception(makeSystemErrorExplicit(err, std::forward(args)...)); +} + +// Helper to throw std::system_error from errno and components of a string +template +[[noreturn]] void throwSystemError(Args&&... args) { + throwSystemErrorExplicit(errno, std::forward(args)...); +} + +// Check a Posix return code (0 on success, error number on error), throw +// on error. +template +void checkPosixError(int err, Args&&... args) { + if (UNLIKELY(err != 0)) { + throwSystemErrorExplicit(err, std::forward(args)...); + } +} + +// Check a Linux kernel-style return code (>= 0 on success, negative error +// number on error), throw on error. +template +void checkKernelError(ssize_t ret, Args&&... args) { + if (UNLIKELY(ret < 0)) { + throwSystemErrorExplicit(int(-ret), std::forward(args)...); + } +} + +// Check a traditional Unix return code (-1 and sets errno on error), throw +// on error. +template +void checkUnixError(ssize_t ret, Args&&... args) { + if (UNLIKELY(ret == -1)) { + throwSystemError(std::forward(args)...); + } +} + +template +void checkUnixErrorExplicit(ssize_t ret, int savedErrno, Args&&... args) { + if (UNLIKELY(ret == -1)) { + throwSystemErrorExplicit(savedErrno, std::forward(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 +void checkFopenError(FILE* fp, Args&&... args) { + if (UNLIKELY(!fp)) { + throwSystemError(std::forward(args)...); + } +} + +template +void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) { + if (UNLIKELY(!fp)) { + throwSystemErrorExplicit(savedErrno, std::forward(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("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 +#include +#include + +#include +#include +#include + +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( + [&]() -> fbstring { std::rethrow_exception(ep); }, + [](auto&& e) { return exceptionStr(e); }); + }, + []() -> fbstring { return ""; }); +} + +template +auto exceptionStr(const E& e) -> typename std:: + enable_if::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 + */ + +#include + +namespace folly { + +template +struct exception_wrapper::arg_type_ + : public arg_type_ {}; +template +struct exception_wrapper::arg_type_ { + using type = Arg; +}; +template +struct exception_wrapper::arg_type_ { + using type = Arg; +}; +template +struct exception_wrapper::arg_type_ { + using type = Arg; +}; +template +struct exception_wrapper::arg_type_ { + using type = Arg; +}; +template +struct exception_wrapper::arg_type_ { + using type = AnyException; +}; +template +struct exception_wrapper::arg_type_ { + using type = AnyException; +}; +template +struct exception_wrapper::arg_type_ { + using type = AnyException; +}; +template +struct exception_wrapper::arg_type_ { + using type = AnyException; +}; + +template +inline Ret exception_wrapper::noop_(Args...) { + return Ret(); +} + +inline std::type_info const* exception_wrapper::uninit_type_( + exception_wrapper const*) { + return &typeid(void); +} + +template +inline exception_wrapper::Buffer::Buffer(in_place_type_t, As&&... as_) { + ::new (static_cast(&buff_)) Ex(std::forward(as_)...); +} + +template +inline Ex& exception_wrapper::Buffer::as() noexcept { + return *static_cast(static_cast(&buff_)); +} +template +inline Ex const& exception_wrapper::Buffer::as() const noexcept { + return *static_cast(static_cast(&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(&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 const*>(&ptr) + ->get(); + return reinterpret_cast(win32ExceptionPtr->exceptionObject); + } +} +inline std::uintptr_t exception_wrapper::ExceptionPtr::as_int_( + std::exception_ptr const&, + AnyException e) noexcept { + return reinterpret_cast(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(exception_or_type_); +} +inline std::type_info const* exception_wrapper::ExceptionPtr::as_type_() const { + return reinterpret_cast(exception_or_type_ - 1); +} + +inline void exception_wrapper::ExceptionPtr::copy_( + exception_wrapper const* from, + exception_wrapper* to) { + ::new (static_cast(&to->eptr_)) ExceptionPtr(from->eptr_); +} +inline void exception_wrapper::ExceptionPtr::move_( + exception_wrapper* from, + exception_wrapper* to) { + ::new (static_cast(&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 +inline void exception_wrapper::InPlace::copy_( + exception_wrapper const* from, + exception_wrapper* to) { + ::new (static_cast(std::addressof(to->buff_.as()))) + Ex(from->buff_.as()); +} +template +inline void exception_wrapper::InPlace::move_( + exception_wrapper* from, + exception_wrapper* to) { + ::new (static_cast(std::addressof(to->buff_.as()))) + Ex(std::move(from->buff_.as())); + delete_(from); +} +template +inline void exception_wrapper::InPlace::delete_(exception_wrapper* that) { + that->buff_.as().~Ex(); + that->vptr_ = &uninit_; +} +template +[[noreturn]] inline void exception_wrapper::InPlace::throw_( + exception_wrapper const* that) { + throw that->buff_.as(); +} +template +inline std::type_info const* exception_wrapper::InPlace::type_( + exception_wrapper const*) { + return &typeid(Ex); +} +template +inline std::exception const* exception_wrapper::InPlace::get_exception_( + exception_wrapper const* that) { + return as_exception_or_null_(that->buff_.as()); +} +template +inline exception_wrapper exception_wrapper::InPlace::get_exception_ptr_( + exception_wrapper const* that) { + try { + throw_(that); + } catch (Ex const& ex) { + return exception_wrapper{std::current_exception(), ex}; + } +} + +template +[[noreturn]] inline void exception_wrapper::SharedPtr::Impl::throw_() + const { + throw ex_; +} +template +inline std::exception const* +exception_wrapper::SharedPtr::Impl::get_exception_() const noexcept { + return as_exception_or_null_(ex_); +} +template +inline exception_wrapper +exception_wrapper::SharedPtr::Impl::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(std::addressof(to->sptr_))) SharedPtr(from->sptr_); +} +inline void exception_wrapper::SharedPtr::move_( + exception_wrapper* from, + exception_wrapper* to) { + ::new (static_cast(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 +inline exception_wrapper::exception_wrapper( + ThrownTag, + in_place_type_t, + As&&... as) + : eptr_{std::make_exception_ptr(Ex(std::forward(as)...)), + reinterpret_cast(std::addressof(typeid(Ex))) + 1u}, + vptr_(&ExceptionPtr::ops_) {} + +template +inline exception_wrapper::exception_wrapper( + OnHeapTag, + in_place_type_t, + As&&... as) + : sptr_{std::make_shared>(std::forward(as)...)}, + vptr_(&SharedPtr::ops_) {} + +template +inline exception_wrapper::exception_wrapper( + InSituTag, + in_place_type_t, + As&&... as) + : buff_{in_place_type, std::forward(as)...}, + vptr_(&InPlace::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 +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 +Ex&& dont_slice(Ex&& ex) { + assert(typeid(ex) == typeid(std::decay_t) || + !"Dynamic and static exception types don't match. Exception would " + "be sliced when storing in exception_wrapper."); + return std::forward(ex); +} +} // namespace exception_wrapper_detail + +template < + class Ex, + class Ex_, + FOLLY_REQUIRES_DEF(Conjunction< + exception_wrapper::IsStdException, + exception_wrapper::IsRegularExceptionType>::value)> +inline exception_wrapper::exception_wrapper(Ex&& ex) + : exception_wrapper{ + PlacementOf{}, + in_place_type, + exception_wrapper_detail::dont_slice(std::forward(ex))} {} + +template < + class Ex, + class Ex_, + FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType::value)> +inline exception_wrapper::exception_wrapper(in_place_t, Ex&& ex) + : exception_wrapper{ + PlacementOf{}, + in_place_type, + exception_wrapper_detail::dont_slice(std::forward(ex))} {} + +template < + class Ex, + typename... As, + FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType::value)> +inline exception_wrapper::exception_wrapper(in_place_type_t, As&&... as) + : exception_wrapper{PlacementOf{}, + in_place_type, + std::forward(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(*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(vptr_->get_exception_(this)); +} +inline std::exception const* exception_wrapper::get_exception() const noexcept { + return vptr_->get_exception_(this); +} + +template +inline Ex* exception_wrapper::get_exception() noexcept { + Ex* object{nullptr}; + with_exception([&](Ex& ex) { object = &ex; }); + return object; +} + +template +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() ? "" : folly::demangle(ti); +} + +template +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 +[[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const { + try { + throw_exception(); + } catch (...) { + std::throw_with_nested(std::forward(ex)); + } +} + +template +struct exception_wrapper::ExceptionTypeOf { + using type = arg_type>; + static_assert( + std::is_reference::value, + "Always catch exceptions by reference."); + static_assert( + !IsConst || std::is_const>::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 +struct exception_wrapper::HandleReduce { + bool* handled_; + + template < + class ThrowFn, + class CatchFn, + FOLLY_REQUIRES(!IsCatchAll::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + using Ex = _t>; + return [th = std::forward(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::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + return [th = std::forward(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 +struct exception_wrapper::HandleStdExceptReduce { + using StdEx = AddConstIf; + + template < + class ThrowFn, + class CatchFn, + FOLLY_REQUIRES(!IsCatchAll::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + using Ex = _t>; + return + [th = std::forward(th), &ca](auto&& continuation) -> StdEx* { + if (auto e = const_cast(th(continuation))) { + if (auto e2 = dynamic_cast>(e)) { + ca(*e2); + } else { + return e; + } + } + return nullptr; + }; + } + + template < + class ThrowFn, + class CatchFn, + FOLLY_REQUIRES(IsCatchAll::value)> + auto operator()(ThrowFn&& th, CatchFn& ca) const { + return [th = std::forward(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 +inline void +exception_wrapper::handle_(std::false_type, This& this_, CatchFns&... fns) { + bool handled = false; + auto impl = exception_wrapper_detail::fold( + HandleReduce::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 +inline void +exception_wrapper::handle_(std::true_type, This& this_, CatchFns&... fns) { + using StdEx = exception_wrapper_detail:: + AddConstIf::value, std::exception>; + auto impl = exception_wrapper_detail::fold( + HandleStdExceptReduce::value>{}, + [&](auto&& continuation) { + return continuation( + const_cast(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 +struct catch_fn { + Fn fn_; + auto operator()(Ex& ex) { + return fn_(ex); + } +}; + +template +inline catch_fn catch_(Ex*, Fn fn) { + return {std::move(fn)}; +} +template +inline Fn catch_(void const*, Fn fn) { + return fn; +} +} // namespace exception_wrapper_detail + +template +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(nullptr), std::move(fn_)); + auto&& all = [&](...) { handled = false; }; + handle_(IsStdException>{}, this_, fn, all); + return handled; +} + +template +inline bool exception_wrapper::with_exception(Fn fn) { + return with_exception_(*this, std::move(fn)); +} +template +inline bool exception_wrapper::with_exception(Fn fn) const { + return with_exception_(*this, std::move(fn)); +} + +template +inline void exception_wrapper::handle(CatchFns... fns) { + using AllStdEx = + exception_wrapper_detail::AllOf...>; + if (!*this) { + onNoExceptionError(__func__); + } + this->handle_(AllStdEx{}, *this, fns...); +} +template +inline void exception_wrapper::handle(CatchFns... fns) const { + using AllStdEx = + exception_wrapper_detail::AllOf...>; + 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 + +#include + +#include + +namespace folly { + +exception_wrapper::VTable const exception_wrapper::uninit_{ + &noop_, + &noop_, + &noop_, + &noop_, + &uninit_type_, + &noop_, + &noop_}; + +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 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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(__VA_ARGS__), long> + +#define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__ + +namespace exception_wrapper_detail { + +template