Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 26e86a9905 | |||
| bab8e08d2f | |||
| 4060ee9f57 | |||
| 5996772433 | |||
| cd45c77fac | |||
| e618d179fe | |||
| 4fa5f48c19 | |||
| 28993ae57d | |||
| 3d61bf87e7 | |||
| 911a4e1aa3 | |||
| beb38dec0e | |||
| 1fbf688897 | |||
| 5436868053 | |||
| 4977ca158f | |||
| 96214ac5f9 | |||
| 21fda909f5 | |||
| 8438d09b82 | |||
| 30a60b52cc | |||
| a93ed143fb | |||
| e054a884ef | |||
| 9bba508134 | |||
| 2d857d6d66 | |||
| 7286336b28 | |||
| 8a0bb6f846 | |||
| 142811c500 | |||
| 3b1cff9337 | |||
| efb4369c74 | |||
| 9d87a3b4ea | |||
| 4714b80631 | |||
| 3cba7363b4 | |||
| b2f592611d | |||
| 8786d0860a | |||
| 9100ccafb3 | |||
| 3fa048ddca | |||
| 8832baa05b | |||
| 11fdf7eaf3 | |||
| 059a86d393 | |||
| c1b4d93a0f | |||
| fb184ceebe | |||
| 69b50074ff | |||
| 7351cd671c | |||
| 26e112f894 | |||
| 6c732515d8 | |||
| 6892923743 | |||
| f8809ffac4 | |||
| 5da402bb2c | |||
| e962ce1e3b | |||
| 19df744bf1 | |||
| 69cc5a92c2 | |||
| cb22cc98f2 | |||
| 22cf5b69ba | |||
| a3fecd18e6 | |||
| 48c6f2999b | |||
| 91297ae63a | |||
| 6f61dcc083 | |||
| 24eca2fab8 | |||
| 9b3c36124f | |||
| 1b017e1dfc | |||
| 32b3aeb94c | |||
| 579d174288 | |||
| 1c850fc6bb | |||
| b35ecf8356 | |||
| 302f663a3f | |||
| c805f0f5aa | |||
| cb99f8b298 | |||
| 915a7efaf5 | |||
| 024e576b0f | |||
| f4a611e95f | |||
| c09e47f792 | |||
| dd6be33016 | |||
| 5af4fb6ba4 | |||
| 0ca4c7658f | |||
| 8cf59d6d2a | |||
| f3d068d6f2 | |||
| 1cfb4a2eae | |||
| 2b64b7981d | |||
| ad95dfdc8c | |||
| 988644159e | |||
| a9fa1ed4f4 | |||
| 3f125b30e1 | |||
| c9e7293a2a | |||
| f5d2c39c58 | |||
| 70bf8e8bb3 | |||
| f04053e1eb | |||
| af510706d7 | |||
| 29fbf7de31 | |||
| 102eef88a0 | |||
| b34d4652d3 | |||
| fcc2a4350a | |||
| 59183ce0a5 | |||
| b1f676d273 | |||
| b2784a1011 | |||
| d67e77c84d | |||
| d680f09932 | |||
| 30b5dd4c2f | |||
| 621f970998 | |||
| d6b70ad309 | |||
| 918e9131ad | |||
| 7f7f397062 | |||
| 3b1437e46c | |||
| 79899f7742 | |||
| 1496bab272 | |||
| 1ebbdb8ea9 | |||
| f861335dc3 | |||
| 769c3c818f | |||
| 910d21da4c | |||
| 6e20956d6d | |||
| e453879d75 | |||
| 98f6b6b337 | |||
| 74b739d74e | |||
| bcba9a19d4 | |||
| 486e166462 | |||
| c6536cf8d3 | |||
| cf41c25cf7 | |||
| b4557fb311 | |||
| f8e6e66ab4 | |||
| 95b42abce3 | |||
| 899a04bb3f | |||
| 5f9a700689 | |||
| a300fd09d3 | |||
| 5973f86c6e | |||
| 1b5afdba26 | |||
| 51d5d1e71d | |||
| a8bc5cc046 | |||
| 86d6170dc9 | |||
| 171131d768 | |||
| d6b4fb4115 | |||
| 014b82b99d |
+182
-256
@@ -1,94 +1,155 @@
|
||||
macOS_tests_steps: &macOS_tests_steps
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Building and running tests in debug mode with coverage
|
||||
command: |
|
||||
make test-debug \
|
||||
SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--enable-code-coverage --build-path .build-test-debug"
|
||||
xcrun llvm-cov show \
|
||||
-instr-profile=.build-test-debug/debug/codecov/default.profdata \
|
||||
.build-test-debug/debug/OpenCombinePackageTests.xctest/Contents/MacOS/OpenCombinePackageTests \
|
||||
> coverage.txt
|
||||
- run:
|
||||
name: Building and running tests in debug mode with TSan
|
||||
command: |
|
||||
make test-debug-sanitize-thread \
|
||||
SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--build-path .build-test-debug-sanitize-thread"
|
||||
- run:
|
||||
name: Building and running tests in release mode
|
||||
command: |
|
||||
make test-release \
|
||||
SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--build-path .build-test-release"
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: make generate-xcodeproj SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors"
|
||||
- run:
|
||||
name: Building for testing on macOS with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild build-for-testing \
|
||||
-scheme OpenCombine-Package \
|
||||
-sdk macosx \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_build-for-testing.log \
|
||||
| xcpretty
|
||||
- store_artifacts:
|
||||
path: xcodebuild_build-for-testing.log
|
||||
- run:
|
||||
name: Testing on macOS with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild test-without-building \
|
||||
-scheme OpenCombine-Package \
|
||||
-sdk macosx \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_test-without-building.log \
|
||||
| xcpretty --report junit -o build/reports/results.xml
|
||||
- store_artifacts:
|
||||
path: xcodebuild_test-without-building.log
|
||||
- store_test_results:
|
||||
path: build/reports
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash) -D DerivedData
|
||||
|
||||
ubuntu_tests_steps: &ubuntu_tests_steps
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Installing dependencies
|
||||
command: |
|
||||
apt update -y
|
||||
apt upgrade -y
|
||||
apt install -y curl python3.8
|
||||
- run:
|
||||
name: "Generating LinuxMain.swift"
|
||||
command: python3.8 utils/discover_tests.py
|
||||
- run:
|
||||
name: Building and running tests in debug mode with coverage
|
||||
command: |
|
||||
make test-debug \
|
||||
SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--enable-code-coverage \
|
||||
--disable-index-store \
|
||||
--build-path .build-test-debug"
|
||||
llvm-cov show \
|
||||
-instr-profile=.build-test-debug/debug/codecov/default.profdata \
|
||||
.build-test-debug/debug/OpenCombinePackageTests.xctest \
|
||||
> coverage.txt
|
||||
- run:
|
||||
name: Building and running tests in debug mode with TSan
|
||||
command: |
|
||||
make test-debug-sanitize-thread \
|
||||
SWIFT_TEST_FLAGS="--disable-index-store \
|
||||
--build-path .build-test-debug-sanitize-thread" \
|
||||
- run:
|
||||
name: Building and running tests in release mode
|
||||
command: |
|
||||
make test-release \
|
||||
SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--build-path .build-test-release"
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
"Execute tests on macOS 10.15.0 (Xcode 11.2.1, Swift 5.1.2)":
|
||||
"Execute tests on macOS 10.15.0 (Xcode 11.3.0, Swift 5.1.3)":
|
||||
macos:
|
||||
xcode: "11.2.1"
|
||||
xcode: "11.3.0"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.2"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Building and running tests in debug mode with coverage
|
||||
command: |
|
||||
make test-debug \
|
||||
SWIFT_TEST_FLAGS="--enable-code-coverage --build-path .build-test-debug"
|
||||
xcrun llvm-cov show \
|
||||
-instr-profile=.build-test-debug/debug/codecov/default.profdata \
|
||||
.build-test-debug/debug/OpenCombinePackageTests.xctest/Contents/MacOS/OpenCombinePackageTests \
|
||||
> coverage.txt
|
||||
- run:
|
||||
name: Building and running tests in debug mode with TSan
|
||||
command: |
|
||||
make test-debug-sanitize-thread \
|
||||
SWIFT_TEST_FLAGS="--build-path .build-test-debug-sanitize-thread"
|
||||
- run:
|
||||
name: Building and running tests in release mode
|
||||
command: |
|
||||
make test-release \
|
||||
SWIFT_TEST_FLAGS="--build-path .build-test-release"
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: make generate-xcodeproj
|
||||
- run:
|
||||
name: Building for testing on macOS 10.15.0 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild build-for-testing \
|
||||
-scheme OpenCombine-Package \
|
||||
-sdk macosx10.15 \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_build-for-testing.log \
|
||||
| xcpretty
|
||||
- store_artifacts:
|
||||
path: xcodebuild_build-for-testing.log
|
||||
- run:
|
||||
name: Testing on macOS 10.15.0 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild test-without-building \
|
||||
-scheme OpenCombine-Package \
|
||||
-sdk macosx10.15 \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_test-without-building.log \
|
||||
| xcpretty --report junit -o build/reports/results.xml
|
||||
- store_artifacts:
|
||||
path: xcodebuild_test-without-building.log
|
||||
- store_test_results:
|
||||
path: build/reports
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash) -D DerivedData
|
||||
SWIFT_VERSION: "5.1.3"
|
||||
<<: *macOS_tests_steps
|
||||
|
||||
"Execute tests on macOS 10.15.0 (Xcode 12.1.0, Swift 5.3.0)":
|
||||
macos:
|
||||
xcode: "12.1.0"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.3.0"
|
||||
<<: *macOS_tests_steps
|
||||
|
||||
"Execute tests on macOS 11.4.0 (Xcode 12.5.0, Swift 5.4.0)":
|
||||
macos:
|
||||
xcode: "12.5.0"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.4.0"
|
||||
<<: *macOS_tests_steps
|
||||
|
||||
"Execute compatibility tests on iOS 13.2.2 (Xcode 11.2.1, Swift 5.1.2)":
|
||||
"Execute compatibility tests on iOS 14.5 (Xcode 12.5.0, Swift 5.4.0)":
|
||||
macos:
|
||||
xcode: "11.2.1"
|
||||
xcode: "12.5.0"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.2"
|
||||
SWIFT_VERSION: "5.4.0"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: make generate-compatibility-xcodeproj
|
||||
- run:
|
||||
name: Building for testing on iOS 13.2.2 with xcodebuild
|
||||
name: Building for testing on iOS 14.5 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild build-for-testing \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone 11,OS=13.2.2" \
|
||||
-destination "platform=iOS Simulator,name=iPhone 12,OS=14.5" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_build-for-testing.log \
|
||||
| xcpretty
|
||||
- store_artifacts:
|
||||
path: xcodebuild_build-for-testing.log
|
||||
- run:
|
||||
name: Testing against Combine on iOS 13.2.2 with xcodebuild
|
||||
name: Testing against Combine on iOS 14.5 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild test-without-building \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone 11,OS=13.2.2" \
|
||||
-destination "platform=iOS Simulator,name=iPhone 12,OS=14.5" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_test-without-building.log \
|
||||
| xcpretty --report junit -o build/reports/results.xml
|
||||
@@ -97,6 +158,18 @@ jobs:
|
||||
- store_test_results:
|
||||
path: build/reports
|
||||
|
||||
"Execute compatibility tests on macOS 11.4.0 (Xcode 12.5.0, Swift 5.4.0)":
|
||||
macos:
|
||||
xcode: "12.5.0"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.4.0"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Testing against Combine on macOS 11.4.0
|
||||
command: |
|
||||
make test-compatibility
|
||||
|
||||
"Execute tests on iOS 9.3 (Xcode 10.2.1, Swift 5.0.1)":
|
||||
macos:
|
||||
xcode: "10.2.1"
|
||||
@@ -125,7 +198,7 @@ jobs:
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: |
|
||||
make generate-xcodeproj
|
||||
make generate-xcodeproj SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors"
|
||||
xcodebuild -scheme OpenCombine-Package -showdestinations
|
||||
- run:
|
||||
name: Building for testing on iOS 9.3 with xcodebuild
|
||||
@@ -158,195 +231,44 @@ jobs:
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash) -D DerivedData
|
||||
|
||||
"Execute tests on iOS 11.4 (Xcode 11.1.0, Swift 5.1.0)":
|
||||
macos:
|
||||
xcode: "11.1.0"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.0"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: |
|
||||
make generate-xcodeproj
|
||||
xcodebuild -scheme OpenCombine-Package -showdestinations
|
||||
- run:
|
||||
name: Building for testing on iOS 11.4 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild build-for-testing \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone X,OS=11.4" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_build-for-testing.log \
|
||||
| xcpretty
|
||||
- store_artifacts:
|
||||
path: xcodebuild_build-for-testing.log
|
||||
- run:
|
||||
name: Testing on iOS 11.4 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild test-without-building \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone X,OS=11.4" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_test-without-building.log \
|
||||
| xcpretty --report junit -o build/reports/results.xml
|
||||
- store_artifacts:
|
||||
path: xcodebuild_test-without-building.log
|
||||
- store_test_results:
|
||||
path: build/reports
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash) -D DerivedData
|
||||
|
||||
"Execute tests on iOS 12.2 (Xcode 11.2.1, Swift 5.1.2)":
|
||||
macos:
|
||||
xcode: "11.2.1"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.2"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: |
|
||||
make generate-xcodeproj
|
||||
xcodebuild -scheme OpenCombine-Package -showdestinations
|
||||
- run:
|
||||
name: Building for testing on iOS 12.2 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild build-for-testing \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone X,OS=12.2" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_build-for-testing.log \
|
||||
| xcpretty
|
||||
- store_artifacts:
|
||||
path: xcodebuild_build-for-testing.log
|
||||
- run:
|
||||
name: Testing on iOS 12.2 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild test-without-building \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone X,OS=12.2" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_test-without-building.log \
|
||||
| xcpretty --report junit -o build/reports/results.xml
|
||||
- store_artifacts:
|
||||
path: xcodebuild_test-without-building.log
|
||||
- store_test_results:
|
||||
path: build/reports
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash) -D DerivedData
|
||||
|
||||
"Execute tests on iOS 13.2.2 (Xcode 11.2.1, Swift 5.1.2)":
|
||||
macos:
|
||||
xcode: "11.2.1"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.2"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: |
|
||||
make generate-xcodeproj
|
||||
xcodebuild -scheme OpenCombine-Package -showdestinations
|
||||
- run:
|
||||
name: Building for testing on iOS 13.2.2 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild build-for-testing \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone 11,OS=13.2.2" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_build-for-testing.log \
|
||||
| xcpretty
|
||||
- store_artifacts:
|
||||
path: xcodebuild_build-for-testing.log
|
||||
- run:
|
||||
name: Testing on iOS 13.2.2 with xcodebuild
|
||||
command: |
|
||||
set -o pipefail \
|
||||
&& xcodebuild test-without-building \
|
||||
-scheme OpenCombine-Package \
|
||||
-destination "platform=iOS Simulator,name=iPhone 11,OS=13.2.2" \
|
||||
-derivedDataPath DerivedData \
|
||||
| tee xcodebuild_test-without-building.log \
|
||||
| xcpretty --report junit -o build/reports/results.xml
|
||||
- store_artifacts:
|
||||
path: xcodebuild_test-without-building.log
|
||||
- store_test_results:
|
||||
path: build/reports
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash) -D DerivedData
|
||||
|
||||
"Execute tests on Ubuntu 18.04 (Swift 5.1.1)":
|
||||
"Execute tests on Ubuntu 18.04 (Swift 5.0)":
|
||||
docker:
|
||||
- image: swift:5.1.1-bionic
|
||||
- image: swift:5.0-bionic
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.1"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Installing dependencies
|
||||
command: |
|
||||
apt update -y
|
||||
apt upgrade -y
|
||||
apt install -y curl
|
||||
- run:
|
||||
name: Building and running tests in debug mode with coverage
|
||||
command: | # We need to run the test command twice because of https://bugs.swift.org/browse/SR-10783
|
||||
make test-debug \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--enable-code-coverage \
|
||||
--build-path .build-test-debug" \
|
||||
> /dev/null 2>&1 \
|
||||
|| true
|
||||
make test-debug \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--enable-code-coverage \
|
||||
--build-path .build-test-debug"
|
||||
llvm-cov show \
|
||||
-instr-profile=.build-test-debug/debug/codecov/default.profdata \
|
||||
.build-test-debug/debug/OpenCombinePackageTests.xctest \
|
||||
> coverage.txt
|
||||
- run:
|
||||
name: Building and running tests in debug mode with TSan
|
||||
command: | # We need to run the test command twice because of https://bugs.swift.org/browse/SR-10783
|
||||
make test-debug-sanitize-thread \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--build-path .build-test-debug-sanitize-thread" \
|
||||
> /dev/null 2>&1 \
|
||||
|| true
|
||||
make test-debug-sanitize-thread \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--build-path .build-test-debug-sanitize-thread" \
|
||||
- run:
|
||||
name: Building and running tests in release mode
|
||||
command: |
|
||||
make test-release \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--build-path .build-test-release"
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
SWIFT_VERSION: "5.0"
|
||||
<<: *ubuntu_tests_steps
|
||||
|
||||
"Execute tests on Ubuntu 18.04 (Swift 5.1)":
|
||||
docker:
|
||||
- image: swift:5.1-bionic
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1"
|
||||
<<: *ubuntu_tests_steps
|
||||
|
||||
"Execute tests on Ubuntu 18.04 (Swift 5.2)":
|
||||
docker:
|
||||
- image: swift:5.2-bionic
|
||||
environment:
|
||||
SWIFT_VERSION: "5.2"
|
||||
<<: *ubuntu_tests_steps
|
||||
|
||||
"Execute tests on Ubuntu 18.04 (Swift 5.3)":
|
||||
docker:
|
||||
- image: swift:5.3-bionic
|
||||
environment:
|
||||
SWIFT_VERSION: "5.3"
|
||||
<<: *ubuntu_tests_steps
|
||||
|
||||
"Execute tests on Ubuntu 18.04 (Swift 5.4)":
|
||||
docker:
|
||||
- image: swift:5.4-bionic
|
||||
environment:
|
||||
SWIFT_VERSION: "5.4"
|
||||
<<: *ubuntu_tests_steps
|
||||
|
||||
"Run SwiftLint and Danger":
|
||||
macos:
|
||||
xcode: "11.2.0"
|
||||
xcode: "11.4.0"
|
||||
environment:
|
||||
HOMEBREW_NO_AUTO_UPDATE: "1"
|
||||
steps:
|
||||
@@ -365,7 +287,7 @@ jobs:
|
||||
|
||||
"Run Pod spec lint":
|
||||
macos:
|
||||
xcode: "11.2.0"
|
||||
xcode: "11.4.0"
|
||||
environment:
|
||||
HOMEBREW_NO_AUTO_UPDATE: "1"
|
||||
steps:
|
||||
@@ -379,19 +301,23 @@ workflows:
|
||||
version: 2
|
||||
"OpenCombine: execute tests on macOS":
|
||||
jobs:
|
||||
- "Execute tests on macOS 10.15.0 (Xcode 11.2.1, Swift 5.1.2)"
|
||||
- "Execute tests on macOS 10.15.0 (Xcode 11.3.0, Swift 5.1.3)"
|
||||
- "Execute tests on macOS 10.15.0 (Xcode 12.1.0, Swift 5.3.0)"
|
||||
- "Execute tests on macOS 11.4.0 (Xcode 12.5.0, Swift 5.4.0)"
|
||||
"OpenCombine: execute compatibility tests":
|
||||
jobs:
|
||||
- "Execute compatibility tests on iOS 13.2.2 (Xcode 11.2.1, Swift 5.1.2)"
|
||||
# - "Execute compatibility tests on iOS 14.5 (Xcode 12.5.0, Swift 5.4.0)"
|
||||
- "Execute compatibility tests on macOS 11.4.0 (Xcode 12.5.0, Swift 5.4.0)"
|
||||
"OpenCombine: execute tests on iOS":
|
||||
jobs:
|
||||
- "Execute tests on iOS 9.3 (Xcode 10.2.1, Swift 5.0.1)"
|
||||
- "Execute tests on iOS 11.4 (Xcode 11.1.0, Swift 5.1.0)"
|
||||
- "Execute tests on iOS 12.2 (Xcode 11.2.1, Swift 5.1.2)"
|
||||
- "Execute tests on iOS 13.2.2 (Xcode 11.2.1, Swift 5.1.2)"
|
||||
"OpenCombine: execute tests on Linux":
|
||||
jobs:
|
||||
- "Execute tests on Ubuntu 18.04 (Swift 5.1.1)"
|
||||
- "Execute tests on Ubuntu 18.04 (Swift 5.0)"
|
||||
- "Execute tests on Ubuntu 18.04 (Swift 5.1)"
|
||||
- "Execute tests on Ubuntu 18.04 (Swift 5.2)"
|
||||
- "Execute tests on Ubuntu 18.04 (Swift 5.3)"
|
||||
- "Execute tests on Ubuntu 18.04 (Swift 5.4)"
|
||||
"OpenCombine: run SwiftLint and Danger":
|
||||
jobs:
|
||||
- "Run SwiftLint and Danger"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
name: SwiftWasm
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
carton_wasmer_test:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: swiftwasm/swiftwasm-action@v5.3
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
/.swiftpm
|
||||
Tests/LinuxMain.swift
|
||||
|
||||
# Created by https://www.gitignore.io/api/Xcode
|
||||
# Edit at https://www.gitignore.io/?templates=Xcode
|
||||
|
||||
@@ -2,6 +2,8 @@ included:
|
||||
- Sources
|
||||
- Tests
|
||||
|
||||
child_config: Tests/.swiftlint.yml
|
||||
|
||||
disabled_rules:
|
||||
- block_based_kvo
|
||||
- class_delegate_protocol
|
||||
@@ -23,6 +25,7 @@ disabled_rules:
|
||||
- trailing_comma
|
||||
- type_body_length
|
||||
- opening_brace
|
||||
- untyped_error_in_catch
|
||||
|
||||
opt_in_rules:
|
||||
- array_init
|
||||
@@ -65,6 +68,10 @@ opt_in_rules:
|
||||
- vertical_whitespace_closing_braces
|
||||
- yoda_condition
|
||||
|
||||
implicit_return:
|
||||
included:
|
||||
- closure
|
||||
|
||||
line_length:
|
||||
warning: 90
|
||||
error: 120
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
SWIFT_VERSION = 5.0
|
||||
OTHER_LDFLAGS = $(inherited) -L"$(TOOLCHAIN_DIR)/usr/lib/swift-$(SWIFT_VERSION)/$(PLATFORM_NAME)" -lobjc -lswiftCore
|
||||
HEADER_SEARCH_PATHS = $(SRCROOT)/Sources/COpenCombineHelpers
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
# 0.12.0 (29 Jan 2021)
|
||||
|
||||
This release adds a new `OpenCombineShim` product that will conditionally re-export either
|
||||
Combine on Apple platforms, or OpenCombine on other platforms. Additionally, `ObservableObject`
|
||||
protocol is now available and working on all platforms.
|
||||
|
||||
A bug with `Timer(timeInterval:repeats:block:)` firing immediately not accounting for the passed
|
||||
`timeInterval` is fixed.
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix `Timer(timeInterval:repeats:block:)` not accounting `timeInterval` ([#196](https://github.com/OpenCombine/OpenCombine/pull/196)) via [@grigorye](https://github.com/grigorye)
|
||||
- Add `OpenCombineShim` product for easier importing ([#197](https://github.com/OpenCombine/OpenCombine/pull/197)) via [@MaxDesiatov](https://github.com/MaxDesiatov)
|
||||
- Implementation for `ObservableObject` with `Mirror` ([#201](https://github.com/OpenCombine/OpenCombine/pull/201)) via [@kateinoigakukun](https://github.com/kateinoigakukun)
|
||||
|
||||
# 0.11.0 (29 Oct 2020)
|
||||
|
||||
This release is compatible with Xcode 12.1.
|
||||
|
||||
### Additions
|
||||
- `Publisher.assigned(to:)` method that accepts a `Published.Publisher`.
|
||||
- New `Publisher.switchToLatest()` overloads.
|
||||
- New `Publisher.flatMap(maxPublishers:_:)` overloads.
|
||||
- `Optional.publisher` property.
|
||||
- New `_Introspection` protocol that allows to track and explore the subscription graph and data flow.
|
||||
|
||||
### Bugfixes
|
||||
- The project should now compile without warnings.
|
||||
- The following entities have been updated to match the behavior of the newest Combine version:
|
||||
- `Subscribers.Assign`
|
||||
- `Publishers.Breakpoint`
|
||||
- `Publishers.Buffer`
|
||||
- `CombineIdentifier`
|
||||
- `Publishers.CompactMap`
|
||||
- `Publishers.Concatenate`
|
||||
- `Publishers.Debounce`
|
||||
- `Publishers.Delay`
|
||||
- `DispatchQueue.SchedulerTimeType.Stride`
|
||||
- `Publishers.Drop`
|
||||
- `Publishers.Encode`
|
||||
- `Publishers.Decode`
|
||||
- `Publishers.Filter`
|
||||
- `Publishers.HandleEvents`
|
||||
- `Publishers.IgnoreOutput`
|
||||
- `Publishers.MeasureInterval`
|
||||
- `OperationQueue` scheduler
|
||||
- `Published`
|
||||
- `Publishers.ReceiveOn`
|
||||
- `Publishers.ReplaceError`
|
||||
- `RunLoop scheduler`
|
||||
- `Publishers.Sequence`
|
||||
- `Subscribers.Sink`
|
||||
- `Publishers.SubscribeOn`
|
||||
- `Publishers.Timeout`
|
||||
- `Timer` publisher
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1 and later.
|
||||
|
||||
# 0.10.2 (23 Oct 2020)
|
||||
|
||||
### Bugfixes
|
||||
- Fixed a crash caused by recursive acquisition of a non-recursive lock in SubbjectSubscriber (#186, thanks @stuaustin for the bug report)
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1 and later.
|
||||
|
||||
# 0.10.1 (4 Oct 2020)
|
||||
|
||||
### Bugfixes
|
||||
- Fixed build errors on Linux with Swift 5.0 and Swift 5.3 toolchains (thanks, @adamleonard and @devmaximilian)
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1 and later.
|
||||
|
||||
# 0.10.0 (28 Jun 2020)
|
||||
|
||||
This release is compatible with Xcode 11.5.
|
||||
|
||||
### Additions
|
||||
- `Timer.publish(every:tolerance:on:in:options:)` (#156, thank you @MaxDesiatov)
|
||||
- `OperationQueue` scheduler (#165)
|
||||
- `Publishers.Timeout` (#164)
|
||||
- `Publishers.Debounce` (#133)
|
||||
|
||||
### Bugfixes
|
||||
- `PassthroughSubject`, `CurrentValueSubject` and `Future` have been rewritten from scratch. They are now faster, more correct and no longer leak subscriptions (#170).
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1 and later.
|
||||
|
||||
# 0.9.0 (12 Jun 2020)
|
||||
|
||||
This release is compatible with Xcode 11.5.
|
||||
|
||||
### Additions
|
||||
- The `Subscribers.Demand` struct can be nicely formatted in LLDB (#146, thank you @mayoff).
|
||||
- `Publishers.SwitchToLatest` (#142).
|
||||
- The `RunLoop` scheduler in `OpenCombineFoundation` (#131).
|
||||
- `Publishers.Catch` and `Publishers.TryCatch` (#140).
|
||||
|
||||
### Bugfixes
|
||||
- Worked around a [bug in the Swift compiler](https://bugs.swift.org/browse/SR-11680) when building the `COpenCombineHelpers` target (#145, thank you @mayoff).
|
||||
- Improved documentation.
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1 and later.
|
||||
|
||||
# 0.8.0 (17 Jan 2020)
|
||||
|
||||
This release is compatible with Xcode 11.3.1.
|
||||
|
||||
### Additions
|
||||
- `Publishers.ReplaceEmpty` (#122, thank you @spadafiva)
|
||||
- `NotificationCenter.Publisher` (#84)
|
||||
- `URLSession.DataTaskPublisher` (#127)
|
||||
- `Publishers.DropUntilOutput` (#136)
|
||||
- `Publishers.CollectByCount` (#137)
|
||||
- `Publishers.AssertNoFailure` (#138)
|
||||
- `Publishers.Buffer` (#143)
|
||||
|
||||
### Bugfixes
|
||||
- Fixed integer overflows in `DispatchQueue.SchedulerTimeType.Stride` (#126, #130)
|
||||
- Fixed the 'default will never be executed' warning on non-Darwin platforms (like Linux) (#129)
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1.
|
||||
|
||||
# 0.7.0 (10 Dec 2019)
|
||||
|
||||
This release is compatible with Xcode 11.2.1.
|
||||
|
||||
### Additions
|
||||
- `Publishers.Delay` (#114)
|
||||
- `Publishers.ReceiveOn` (#115)
|
||||
- `Publishers.SubscribeOn` (#116)
|
||||
- `Publishers.MeasureInterval` (#117)
|
||||
- `Publishers.Breakpoint` (#118)
|
||||
- `Publishers.HandleEvents` (#118)
|
||||
- `Publishers.Concatenate` (#90)
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1.
|
||||
|
||||
# 0.6.0 (26 Nov 2019)
|
||||
|
||||
This release is compatible with Xcode 11.2.1.
|
||||
|
||||
### Thread safety
|
||||
- `Publishers.IgnoreOutput` has been audited for thread safety (#88)
|
||||
- `Publishers.DropWhile` and `Publishers.TryDropWhile` have been audited for thread safety (#87)
|
||||
|
||||
### Additions
|
||||
- `Publishers.Output` (#91)
|
||||
- `Record` (#100)
|
||||
- `Publishers.RemoveDuplicates`, `Publishers.TryRemoveDuplicates` (#89)
|
||||
- `Publishers.PrefixWhile`, `Publishers.TryPrefixWhile` (#89)
|
||||
- `Future` (#107, thanks @MaxDesiatov!)
|
||||
|
||||
### Bugfixes
|
||||
- The behavior of the `Publishers.Encode` and `Publishers.Decode` subscriptions is fixed (#112)
|
||||
- The behavior of the `Publishers.IgnoreOutput` subscription is fixed (#88)
|
||||
- The behavior of the `Publishers.Print` subscription is fixed (#92)
|
||||
- The behavior of the `Publishers.ReplaceError` subscription is fixed (#89)
|
||||
- The behavior of the `Publishers.Filter` and `Publishers.TryFilter` subscriptions is fixed (#89)
|
||||
- The behavior of the `Publishers.CompactMap` and `Publishers.TryCompactMap` subscriptions is fixed (#89)
|
||||
- The behavior of the `Publishers.Multicast` subscription is fixed (#110)
|
||||
- `Publishers.FlatMap` is reimplemented from scratch. Its behavior is fixed in many ways, it now fully matches that of Combine (#89)
|
||||
- `@Published` property wrapper is fixed! (#112)
|
||||
- The behavior of `DispatchQueue.SchedulerTimeType` is fixed to match that of the latest SDKs (#96)
|
||||
- OpenCombine is now usable on 32 bit platforms. Why? Because we can.
|
||||
|
||||
|
||||
### Known issues
|
||||
- The default implementation of the `objectWillChange` requirement of the `ObservableObject` protocol is not available in Swift 5.1.
|
||||
|
||||
# 0.5.0 (17 Oct 2019)
|
||||
|
||||
This release is compatible with Xcode 11.1.
|
||||
|
||||
### Additions
|
||||
- `Publishers.MapKeyPath` (#71)
|
||||
- `Publishers.Reduce` (#76)
|
||||
- `Publishers.TryReduce` (#76)
|
||||
- `Publishers.Last` (#76)
|
||||
- `Publishers.LastWhere` (#76)
|
||||
- `Publishers.TryLastWhere` (#76)
|
||||
- `Publishers.AllSatisfy` (#76)
|
||||
- `Publishers.TryAllSatisfy` (#76)
|
||||
- `Publishers.Contains` (#76)
|
||||
- `Publishers.ContainsWhere` (#76)
|
||||
- `Publishers.TryContainsWhere` (#76)
|
||||
- `Publishers.Collect` (#76)
|
||||
- `Publishers.Comparison` (#76)
|
||||
- `Publishers.Drop` (#70, thank you @5sw!)
|
||||
- `Publishers.Scan` (#83, thank you @epatey!)
|
||||
- `Publishers.TryScan` (#83, thank you @epatey!)
|
||||
|
||||
### Bugfixes
|
||||
- `Publishers.Print` doesn't print a redundant whitespace anymore.
|
||||
|
||||
### Known issues
|
||||
- `@Published` property wrapper doesn't work yet
|
||||
|
||||
# 0.4.0 (8 Oct 2019)
|
||||
|
||||
This release is compatible with Xcode 11.1.
|
||||
|
||||
### Thread safety
|
||||
- `SubjectSubscriber` (which is used when you subscribe a subject to a publisher) has been audited for thread-safety
|
||||
- `Publishers.Multicast` has been audited for thread safety (#63)
|
||||
- `Publishers.TryMap` has been audited for thread safety
|
||||
- `Just` has been audited for thread safety
|
||||
- `Optional.Publisher` has been audited for thread safety
|
||||
- `Publishers.Sequence` has been audited for thread safety
|
||||
- `Publishers.ReplaceError` has been audited for thread safety
|
||||
- `Subscribers.Assign` has been audited for thread safety
|
||||
- `Subscribers.Sink` has been audited for thread safety
|
||||
|
||||
### Bugfixes
|
||||
- The semantics of `Publishers.Print`, `Publishers.TryMap` have been fixed
|
||||
- Fix `iterator.next()` being called twice in `Publishers.Sequence` (#62)
|
||||
- The default initializer of `CombineIdentifier` (the one that takes no arguments) is now much faster (#66, #69)
|
||||
- When `Publishers.Sequence` subscription is cancelled while it emits values, the cancellation is respected (#73, thanks @5sw!)
|
||||
|
||||
### Additions
|
||||
- `DispatchQueueScheduler` (#46)
|
||||
- `Equatable` conformances for `First`, `ReplaceError`
|
||||
- Added `eraseToAnyPublisher()` method (#59, thanks @evyasafhouzz for reporting!)
|
||||
- `Publishers.MakeConnectable` (#61)
|
||||
- `Publishers.Autoconnect` (#60)
|
||||
- `Publishers.Share` (#60)
|
||||
|
||||
### Known issues
|
||||
- `@Published` property wrapper doesn't work yet
|
||||
|
||||
# 0.3.0 (13 Sep 2019)
|
||||
|
||||
Among other things this release is compatible with Xcode 11.0 GM seed.
|
||||
|
||||
### Bugfixes
|
||||
- Store newly send value in internal variable inside CurrentValueObject (#39, thanks @FranzBusch!)
|
||||
|
||||
### Additions
|
||||
- `Filter`/`TryFilter` (#22, thanks @spadafiva!)
|
||||
- `First`/`FirstWhere`/`TryFirstWhere` (#22, thanks again @spadafiva!)
|
||||
- `CompactMap`/`TryCompacrMap` (#32)
|
||||
- `IgnoreOutput` (#44, thanks @epatey!)
|
||||
- `ReplaceError` (#50, thanks @vladiulianbogdan!)
|
||||
- `FlatMap` (#45, thanks again @epatey!)
|
||||
|
||||
### Known issues
|
||||
- `@Published` property wrapper doesn't work yet
|
||||
|
||||
# 0.2.0 (31 Jul 2019)
|
||||
|
||||
Updated for the newest Xcode 11.0 beta 5
|
||||
|
||||
# 0.1.0 (4 Jul 2019)
|
||||
|
||||
The first pre-pre-pre-alpha release is here!
|
||||
|
||||
Lots of stuff still unimplemented.
|
||||
|
||||
For now we have:
|
||||
|
||||
- `Just`
|
||||
- `Publishers.Decode`
|
||||
- `Publishers.DropWhile`
|
||||
- `Publishers.Empty`
|
||||
- `Publishers.Encode`
|
||||
- `Publishers.Fail`
|
||||
- `Publishers.Map`
|
||||
- `Publishers.Multicast`
|
||||
- `Publishers.Once`
|
||||
- `Publishers.Optional`
|
||||
- `Publishers.Print`
|
||||
- `Publishers.Sequence`
|
||||
- `Subscribers.Assign`
|
||||
- `Subscribers.Completion`
|
||||
- `Subscribers.Demand`
|
||||
- `Subscribers.Sink`
|
||||
- `AnyCancellable`
|
||||
- `AnyPublisher`
|
||||
- `AnySubject`
|
||||
- `AnySubscriber`
|
||||
- `Cancellable`
|
||||
- `CombineIdentifier`
|
||||
- `ConnectablePublisher`
|
||||
- `CurrentValueSubject`
|
||||
- `CustomCombineIdentifierConvertible`
|
||||
- `ImmediateScheduler`
|
||||
- `PassthroughSubject`
|
||||
- `Publisher`
|
||||
- `Result`
|
||||
- `Scheduler`
|
||||
- `Subject`
|
||||
- `Subscriber`
|
||||
- `Subscription`
|
||||
@@ -1,28 +0,0 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "COpenCombineHelpers"
|
||||
spec.version = "0.5.0"
|
||||
spec.summary = "C++ Helpers for OpenCombine"
|
||||
|
||||
spec.description = <<-DESC
|
||||
C++ helpers necessary for the implementation of OpenCombine
|
||||
DESC
|
||||
|
||||
spec.homepage = "https://github.com/broadwaylamb/OpenCombine/"
|
||||
spec.license = "MIT"
|
||||
|
||||
spec.authors = { "Sergej Jaskiewicz" => "jaskiewiczs@icloud.com" }
|
||||
spec.source = { :git => "https://github.com/broadwaylamb/OpenCombine.git", :tag => "#{spec.version}" }
|
||||
|
||||
spec.osx.deployment_target = "10.10"
|
||||
spec.ios.deployment_target = "8.0"
|
||||
spec.watchos.deployment_target = "2.0"
|
||||
spec.tvos.deployment_target = "9.0"
|
||||
|
||||
spec.header_mappings_dir = "Sources/COpenCombineHelpers/include"
|
||||
spec.source_files = "Sources/COpenCombineHelpers/**/*.{cpp,h}"
|
||||
spec.libraries = "c++"
|
||||
|
||||
spec.pod_target_xcconfig = {
|
||||
"DEFINES_MODULE" => "YES"
|
||||
}
|
||||
end
|
||||
@@ -1,2 +1 @@
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -DOPENCOMBINE_COMPATIBILITY_TEST
|
||||
#include "OpenCombineSPM.xcconfig"
|
||||
+3
-3
@@ -66,10 +66,10 @@ do {
|
||||
}
|
||||
}
|
||||
|
||||
SwiftLint.lint(inline: true,
|
||||
SwiftLint.lint(.all(directory: nil),
|
||||
inline: true,
|
||||
configFile: ".swiftlint.yml",
|
||||
strict: true,
|
||||
lintAllFiles: true)
|
||||
strict: true)
|
||||
|
||||
if danger.warnings.isEmpty, danger.fails.isEmpty {
|
||||
markdown("LGTM")
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ GEM
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.5)
|
||||
emoji_regex (1.0.1)
|
||||
excon (0.68.0)
|
||||
excon (0.71.0)
|
||||
faraday (0.17.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
@@ -93,7 +93,7 @@ GEM
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
json (2.2.0)
|
||||
json (2.3.1)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.1)
|
||||
mime-types (3.3)
|
||||
|
||||
@@ -8,10 +8,8 @@ debug:
|
||||
release:
|
||||
$(SWIFT_EXE) build -c release $(SWIFT_BUILD_FLAGS)
|
||||
|
||||
test-debug: export ASAN_OPTIONS=detect_leaks=0
|
||||
|
||||
test-debug:
|
||||
$(SWIFT_EXE) test -c debug --sanitize address $(SWIFT_BUILD_FLAGS) $(SWIFT_TEST_FLAGS)
|
||||
$(SWIFT_EXE) test -c debug $(SWIFT_BUILD_FLAGS) $(SWIFT_TEST_FLAGS)
|
||||
|
||||
test-debug-sanitize-thread:
|
||||
$(SWIFT_EXE) test -c debug --sanitize thread $(SWIFT_BUILD_FLAGS) $(SWIFT_TEST_FLAGS)
|
||||
@@ -26,14 +24,11 @@ test-compatibility:
|
||||
$(SWIFT_EXE) test -Xswiftc -DOPENCOMBINE_COMPATIBILITY_TEST
|
||||
|
||||
generate-compatibility-xcodeproj:
|
||||
$(SWIFT_EXE) package generate-xcodeproj \
|
||||
--xcconfig-overrides .xcconfigs/Combine-Compatibility.xcconfig; \
|
||||
$(SWIFT_EXE) package generate-xcodeproj --xcconfig-overrides Combine-Compatibility.xcconfig; \
|
||||
open OpenCombine.xcodeproj
|
||||
|
||||
generate-xcodeproj:
|
||||
$(SWIFT_EXE) package $(SWIFT_BUILD_FLAGS) generate-xcodeproj \
|
||||
--enable-code-coverage \
|
||||
--xcconfig-overrides .xcconfigs/OpenCombineSPM.xcconfig
|
||||
$(SWIFT_EXE) package $(SWIFT_BUILD_FLAGS) generate-xcodeproj --enable-code-coverage
|
||||
|
||||
gyb:
|
||||
$(shell ./utils/recursively_gyb.sh)
|
||||
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "OpenCombine"
|
||||
spec.version = "0.5.0"
|
||||
spec.version = "0.12.0"
|
||||
spec.summary = "Open source implementation of Apple's Combine framework for processing values over time."
|
||||
|
||||
spec.description = <<-DESC
|
||||
@@ -20,6 +20,8 @@ Pod::Spec.new do |spec|
|
||||
spec.watchos.deployment_target = "2.0"
|
||||
spec.tvos.deployment_target = "9.0"
|
||||
|
||||
spec.source_files = "Sources/OpenCombine/**/*.swift"
|
||||
spec.dependency "COpenCombineHelpers"
|
||||
end
|
||||
spec.source_files = "Sources/COpenCombineHelpers/**/*.{h,cpp}", "Sources/OpenCombine/**/*.swift"
|
||||
spec.public_header_files = "Sources/COpenCombineHelpers/include/*.h"
|
||||
|
||||
spec.libraries = "c++"
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "OpenCombineDispatch"
|
||||
spec.version = "0.5.0"
|
||||
spec.summary = "OpenCombine Dispatching"
|
||||
spec.version = "0.12.0"
|
||||
spec.summary = "OpenCombine + Dispatch interoperability"
|
||||
|
||||
spec.description = <<-DESC
|
||||
Extends `DispatchQueue` with new methods and nested types.
|
||||
Extends `DispatchQueue` with conformance to the `Scheduler` protocol
|
||||
DESC
|
||||
|
||||
spec.homepage = "https://github.com/broadwaylamb/OpenCombine/"
|
||||
@@ -21,5 +21,5 @@ Pod::Spec.new do |spec|
|
||||
spec.tvos.deployment_target = "9.0"
|
||||
|
||||
spec.source_files = "Sources/OpenCombineDispatch/**/*.swift"
|
||||
spec.dependency "OpenCombine"
|
||||
end
|
||||
spec.dependency "OpenCombine", '>= 0.10.2'
|
||||
end
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "OpenCombineFoundation"
|
||||
spec.version = "0.12.0"
|
||||
spec.summary = "OpenCombine + OpenCombineFoundation interoperability"
|
||||
|
||||
spec.description = <<-DESC
|
||||
Adds publishers to Foundation types like NotificationCenter, URLSession etc.
|
||||
DESC
|
||||
|
||||
spec.homepage = "https://github.com/broadwaylamb/OpenCombine/"
|
||||
spec.license = "MIT"
|
||||
|
||||
spec.authors = { "Sergej Jaskiewicz" => "jaskiewiczs@icloud.com" }
|
||||
spec.source = { :git => "https://github.com/broadwaylamb/OpenCombine.git", :tag => "#{spec.version}" }
|
||||
|
||||
spec.swift_version = "5.0"
|
||||
|
||||
spec.osx.deployment_target = "10.10"
|
||||
spec.ios.deployment_target = "8.0"
|
||||
spec.watchos.deployment_target = "2.0"
|
||||
spec.tvos.deployment_target = "9.0"
|
||||
|
||||
spec.source_files = "Sources/OpenCombineFoundation/**/*.swift"
|
||||
spec.dependency "OpenCombine", '>= 0.10.2'
|
||||
end
|
||||
+76
-8
@@ -1,21 +1,89 @@
|
||||
// swift-tools-version:5.0
|
||||
// swift-tools-version:5.3
|
||||
|
||||
import PackageDescription
|
||||
|
||||
// This list should be updated whenever SwiftPM adds support for a new platform.
|
||||
// See: https://bugs.swift.org/browse/SR-13814
|
||||
let supportedPlatforms: [Platform] = [
|
||||
.macOS,
|
||||
.iOS,
|
||||
.watchOS,
|
||||
.tvOS,
|
||||
.linux,
|
||||
.android,
|
||||
// Disable Windows because of https://bugs.swift.org/browse/SR-13817
|
||||
// .windows,
|
||||
.wasi,
|
||||
]
|
||||
|
||||
let package = Package(
|
||||
name: "OpenCombine",
|
||||
products: [
|
||||
.library(name: "OpenCombine", targets: ["OpenCombine"]),
|
||||
.library(name: "OpenCombineDispatch", targets: ["OpenCombineDispatch"]),
|
||||
.library(name: "OpenCombineFoundation", targets: ["OpenCombineFoundation"]),
|
||||
.library(name: "OpenCombineShim", targets: ["OpenCombineShim"]),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "COpenCombineHelpers", cxxSettings: [.headerSearchPath(".")]),
|
||||
.target(name: "OpenCombine", dependencies: ["COpenCombineHelpers"]),
|
||||
.target(name: "COpenCombineHelpers"),
|
||||
.target(
|
||||
name: "OpenCombine",
|
||||
dependencies: [
|
||||
.target(name: "COpenCombineHelpers",
|
||||
condition: .when(platforms: supportedPlatforms.except([.wasi])))
|
||||
],
|
||||
exclude: [
|
||||
"Publishers/Publishers.Encode.swift.gyb",
|
||||
"Publishers/Publishers.MapKeyPath.swift.gyb",
|
||||
"Publishers/Publishers.Catch.swift.gyb"
|
||||
],
|
||||
swiftSettings: [.define("WASI", .when(platforms: [.wasi]))]
|
||||
),
|
||||
.target(name: "OpenCombineDispatch", dependencies: ["OpenCombine"]),
|
||||
.testTarget(name: "OpenCombineTests",
|
||||
dependencies: ["OpenCombine",
|
||||
"OpenCombineDispatch"],
|
||||
swiftSettings: [.unsafeFlags(["-enable-testing"])])
|
||||
.target(
|
||||
name: "OpenCombineFoundation",
|
||||
dependencies: [
|
||||
"OpenCombine",
|
||||
.target(name: "COpenCombineHelpers",
|
||||
condition: .when(platforms: supportedPlatforms.except([.wasi])))
|
||||
]
|
||||
),
|
||||
.target(
|
||||
name: "OpenCombineShim",
|
||||
dependencies: [
|
||||
"OpenCombine",
|
||||
.target(name: "OpenCombineDispatch",
|
||||
condition: .when(platforms: supportedPlatforms.except([.wasi]))),
|
||||
.target(name: "OpenCombineFoundation",
|
||||
condition: .when(platforms: supportedPlatforms.except([.wasi])))
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "OpenCombineTests",
|
||||
dependencies: [
|
||||
"OpenCombine",
|
||||
.target(name: "OpenCombineDispatch",
|
||||
condition: .when(platforms: supportedPlatforms.except([.wasi]))),
|
||||
.target(name: "OpenCombineFoundation",
|
||||
condition: .when(platforms: supportedPlatforms.except([.wasi]))),
|
||||
],
|
||||
swiftSettings: [
|
||||
.unsafeFlags(["-enable-testing"]),
|
||||
.define("WASI", .when(platforms: [.wasi]))
|
||||
]
|
||||
)
|
||||
],
|
||||
cxxLanguageStandard: .cxx14
|
||||
cxxLanguageStandard: .cxx1z
|
||||
)
|
||||
|
||||
// MARK: Helpers
|
||||
|
||||
extension Array where Element == Platform {
|
||||
func except(_ exceptions: [Platform]) -> [Platform] {
|
||||
// See: https://bugs.swift.org/browse/SR-13813
|
||||
let exceptionsDescriptions = exceptions.map(String.init(describing:))
|
||||
return filter { platform in
|
||||
!exceptionsDescriptions.contains(String(describing: platform))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// swift-tools-version:5.0
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "OpenCombine",
|
||||
products: [
|
||||
.library(name: "OpenCombine", targets: ["OpenCombine"]),
|
||||
.library(name: "OpenCombineDispatch", targets: ["OpenCombineDispatch"]),
|
||||
.library(name: "OpenCombineFoundation", targets: ["OpenCombineFoundation"]),
|
||||
.library(name: "OpenCombineShim", targets: ["OpenCombineShim"]),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "COpenCombineHelpers"),
|
||||
.target(name: "OpenCombine", dependencies: ["COpenCombineHelpers"]),
|
||||
.target(name: "OpenCombineDispatch", dependencies: ["OpenCombine"]),
|
||||
.target(name: "OpenCombineFoundation", dependencies: ["OpenCombine",
|
||||
"COpenCombineHelpers"]),
|
||||
.target(
|
||||
name: "OpenCombineShim",
|
||||
dependencies: [
|
||||
"OpenCombine",
|
||||
"OpenCombineDispatch",
|
||||
"OpenCombineFoundation",
|
||||
]
|
||||
),
|
||||
.testTarget(name: "OpenCombineTests",
|
||||
dependencies: ["OpenCombine",
|
||||
"OpenCombineDispatch",
|
||||
"OpenCombineFoundation"],
|
||||
swiftSettings: [.unsafeFlags(["-enable-testing"])])
|
||||
],
|
||||
cxxLanguageStandard: .cxx1z
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
// swift-tools-version:5.1
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "OpenCombine",
|
||||
products: [
|
||||
.library(name: "OpenCombine", targets: ["OpenCombine"]),
|
||||
.library(name: "OpenCombineDispatch", targets: ["OpenCombineDispatch"]),
|
||||
.library(name: "OpenCombineFoundation", targets: ["OpenCombineFoundation"]),
|
||||
.library(name: "OpenCombineShim", targets: ["OpenCombineShim"]),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "COpenCombineHelpers"),
|
||||
.target(name: "OpenCombine", dependencies: ["COpenCombineHelpers"]),
|
||||
.target(name: "OpenCombineDispatch", dependencies: ["OpenCombine"]),
|
||||
.target(name: "OpenCombineFoundation", dependencies: ["OpenCombine",
|
||||
"COpenCombineHelpers"]),
|
||||
.target(
|
||||
name: "OpenCombineShim",
|
||||
dependencies: [
|
||||
"OpenCombine",
|
||||
"OpenCombineDispatch",
|
||||
"OpenCombineFoundation",
|
||||
]
|
||||
),
|
||||
.testTarget(name: "OpenCombineTests",
|
||||
dependencies: ["OpenCombine",
|
||||
"OpenCombineDispatch",
|
||||
"OpenCombineFoundation"],
|
||||
swiftSettings: [.unsafeFlags(["-enable-testing"])])
|
||||
],
|
||||
cxxLanguageStandard: .cxx1z
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
// swift-tools-version:5.2
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "OpenCombine",
|
||||
products: [
|
||||
.library(name: "OpenCombine", targets: ["OpenCombine"]),
|
||||
.library(name: "OpenCombineDispatch", targets: ["OpenCombineDispatch"]),
|
||||
.library(name: "OpenCombineFoundation", targets: ["OpenCombineFoundation"]),
|
||||
.library(name: "OpenCombineShim", targets: ["OpenCombineShim"]),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "COpenCombineHelpers"),
|
||||
.target(name: "OpenCombine", dependencies: ["COpenCombineHelpers"]),
|
||||
.target(name: "OpenCombineDispatch", dependencies: ["OpenCombine"]),
|
||||
.target(name: "OpenCombineFoundation", dependencies: ["OpenCombine",
|
||||
"COpenCombineHelpers"]),
|
||||
.target(
|
||||
name: "OpenCombineShim",
|
||||
dependencies: [
|
||||
"OpenCombine",
|
||||
"OpenCombineDispatch",
|
||||
"OpenCombineFoundation",
|
||||
]
|
||||
),
|
||||
.testTarget(name: "OpenCombineTests",
|
||||
dependencies: ["OpenCombine",
|
||||
"OpenCombineDispatch",
|
||||
"OpenCombineFoundation"],
|
||||
swiftSettings: [.unsafeFlags(["-enable-testing"])])
|
||||
],
|
||||
cxxLanguageStandard: .cxx1z
|
||||
)
|
||||
@@ -1,38 +1,49 @@
|
||||
# OpenCombine
|
||||
[](https://circleci.com/gh/broadwaylamb/OpenCombine/tree/master)
|
||||
[](https://codecov.io/gh/broadwaylamb/OpenCombine)
|
||||
[](https://circleci.com/gh/OpenCombine/OpenCombine)
|
||||
[](https://codecov.io/gh/OpenCombine/OpenCombine)
|
||||

|
||||

|
||||

|
||||

|
||||
[<img src="https://img.shields.io/badge/slack-OpenCombine-yellow.svg?logo=slack">](https://join.slack.com/t/opencombine/shared_invite/enQtNzE2MjE5NzkxODI0LTYxMjkzNDUxZWViZWI1Njc2YjBhODgxNjRjOTdkZTcxOGU2ZjJjZjYxMGI3NWZkN2RkNGFmZTUzNmU3MGE2ZWM)
|
||||
|
||||
Open-source implementation of Apple's [Combine](https://developer.apple.com/documentation/combine) framework for processing values over time.
|
||||
|
||||
The main goal of this project is to provide a compatible, reliable and efficient implementation which can be used on Apple's operating systems before macOS 10.15 and iOS 13, as well as Linux and Windows.
|
||||
|
||||
The project is in early development.
|
||||
The main goal of this project is to provide a compatible, reliable and efficient implementation which can be used on Apple's operating systems before macOS 10.15 and iOS 13, as well as Linux and WebAssembly.
|
||||
|
||||
### Installation
|
||||
`OpenCombine` contains two public targets: `OpenCombine` and `OpenCombineDispatch` (the third one, `COpenCombineHelpers`, is considered private. Don't import it in your projects).
|
||||
`OpenCombine` contains three public targets: `OpenCombine`, `OpenCombineFoundation` and `OpenCombineDispatch` (the fourth one, `COpenCombineHelpers`, is considered private. Don't import it in your projects).
|
||||
|
||||
OpenCombine itself does not have any dependencies. Not even Foundation or Dispatch. If you want to use OpenCombine with Dispatch (for example for using `DispatchQueue` as `Scheduler` for operators like `debounce`, `receive(on:)` etc.), you will need to import both `OpenCombine` and `OpenCombineDispatch`.
|
||||
OpenCombine itself does not have any dependencies. Not even Foundation or Dispatch. If you want to use OpenCombine with Dispatch (for example for using `DispatchQueue` as `Scheduler` for operators like `debounce`, `receive(on:)` etc.), you will need to import both `OpenCombine` and `OpenCombineDispatch`. The same applies to Foundation: if you want to use, for instance, `NotificationCenter` or `URLSession` publishers, you'll need to also import `OpenCombineFoundation`.
|
||||
|
||||
If you develop code for multiple platforms, you may find it more convenient to import the
|
||||
`OpenCombineShim` module instead. It conditionally re-exports Combine on Apple platforms (if
|
||||
available), and all OpenCombine modules on other platforms. You can import `OpenCombineShim` only
|
||||
when using SwiftPM. It is not currently available for CocoaPods.
|
||||
|
||||
##### Swift Package Manager
|
||||
###### Swift Package
|
||||
To add `OpenCombine` to your [SPM](https://swift.org/package-manager/) package, add the `OpenCombine` package to the list of package and target dependencies in your `Package.swift` file.
|
||||
To add `OpenCombine` to your [SwiftPM](https://swift.org/package-manager/) package, add the `OpenCombine` package to the list of package and target dependencies in your `Package.swift` file. `OpenCombineDispatch` and `OpenCombineFoundation` products are currently not supported on WebAssembly. If your project targets WebAssembly exclusively, you should omit them from the list of your dependencies. If it targets multiple platforms including WebAssembly, depend on them only on non-WebAssembly platforms with [conditional target dependencies](https://github.com/apple/swift-evolution/blob/main/proposals/0273-swiftpm-conditional-target-dependencies.md).
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/broadwaylamb/OpenCombine.git", from: "0.5.0")
|
||||
.package(url: "https://github.com/OpenCombine/OpenCombine.git", from: "0.12.0")
|
||||
],
|
||||
targets: [
|
||||
.target(name: "MyAwesomePackage", dependencies: ["OpenCombine", "OpenCombineDispatch"])
|
||||
.target(
|
||||
name: "MyAwesomePackage",
|
||||
dependencies: [
|
||||
"OpenCombine",
|
||||
.product(name: "OpenCombineFoundation", package: "OpenCombine"),
|
||||
.product(name: "OpenCombineDispatch", package: "OpenCombine")
|
||||
]
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
###### Xcode
|
||||
`OpenCombine` can also be added as a SPM dependency directly in your Xcode project *(requires Xcode 11 upwards)*.
|
||||
`OpenCombine` can also be added as a SwiftPM dependency directly in your Xcode project *(requires Xcode 11 upwards)*.
|
||||
|
||||
To do so, open Xcode, use **File** → **Swift Packages** → **Add Package Dependency…**, enter the [repository URL](https://github.com/broadwaylamb/OpenCombine.git), choose the latest available version, and activate the checkboxes:
|
||||
To do so, open Xcode, use **File** → **Swift Packages** → **Add Package Dependency…**, enter the [repository URL](https://github.com/OpenCombine/OpenCombine.git), choose the latest available version, and activate the checkboxes:
|
||||
|
||||
<p align="center">
|
||||
<img alt="Select the OpenCombine and OpenCombineDispatch targets"
|
||||
@@ -43,17 +54,18 @@ To do so, open Xcode, use **File** → **Swift Packages** → **Add Package Depe
|
||||
To add `OpenCombine` to a project using [CocoaPods](https://cocoapods.org/), add `OpenCombine` and `OpenCombineDispatch` to the list of target dependencies in your `Podfile`.
|
||||
|
||||
```ruby
|
||||
pod 'OpenCombine', '~> 0.5.0'
|
||||
pod 'OpenCombineDispatch', '~> 0.5.0'
|
||||
pod 'OpenCombine', '~> 0.12.0'
|
||||
pod 'OpenCombineDispatch', '~> 0.12.0'
|
||||
pod 'OpenCombineFoundation', '~> 0.12.0'
|
||||
```
|
||||
|
||||
### Contributing
|
||||
|
||||
In order to work on this project you will need Xcode 10.2 and Swift 5.0 or later.
|
||||
|
||||
Please refer to the [issue #1](https://github.com/broadwaylamb/OpenCombine/issues/1) for the list of operators that remain unimplemented, as well as the [RemainingCombineInterface.swift](https://github.com/broadwaylamb/OpenCombine/blob/master/RemainingCombineInterface.swift) file. The latter contains the generated interface of Apple's Combine from the latest Xcode 11 version. When the functionality is implemented in OpenCombine, it should be removed from the RemainingCombineInterface.swift file.
|
||||
Please refer to the [issue #1](https://github.com/OpenCombine/OpenCombine/issues/1) for the list of operators that remain unimplemented, as well as the [RemainingCombineInterface.swift](https://github.com/OpenCombine/OpenCombine/blob/master/RemainingCombineInterface.swift) file. The latter contains the generated interface of Apple's Combine from the latest Xcode 11 version. When the functionality is implemented in OpenCombine, it should be removed from the RemainingCombineInterface.swift file.
|
||||
|
||||
You can refer to [this gist](https://gist.github.com/broadwaylamb/c2c8550d76b3ff851c4c1dbf0a872e26) to observe Apple's Combine API changes between different Xcode (beta) versions, or to [this gist](https://gist.github.com/broadwaylamb/82dc2ce4ffbe06527c2c352b8f10910f) to see the relevant contents of the .swiftinterface file for Combine.
|
||||
You can refer to [this repo](https://github.com/OpenCombine/combine-interfaces) to observe Apple's Combine API and documentation changes between different Xcode (beta) versions.
|
||||
|
||||
You can run compatibility tests against Apple's Combine. In order to do that you will need either macOS 10.14 with iOS 13 simulator installed (since the only way we can get Apple's Combine on macOS 10.14 is using the simulator), or macOS 10.15 (Apple's Combine is bundled with the OS). Execute the following command from the root of the package:
|
||||
|
||||
@@ -63,7 +75,29 @@ $ make test-compatibility
|
||||
|
||||
Or enable the `-DOPENCOMBINE_COMPATIBILITY_TEST` compiler flag in Xcode's build settings. Note that on iOS only the latter will work.
|
||||
|
||||
> NOTE: Before starting to work on some feature, please consult the [GitHub project](https://github.com/broadwaylamb/OpenCombine/projects/2) to make sure that nobody's already making progress on the same feature! If not, then please create a draft PR to indicate that you're beginning your work.
|
||||
> NOTE: Before starting to work on some feature, please consult the [GitHub project](https://github.com/OpenCombine/OpenCombine/projects/2) to make sure that nobody's already making progress on the same feature! If not, then please create a draft PR to indicate that you're beginning your work.
|
||||
|
||||
#### Releasing a new version
|
||||
|
||||
1. Create a new branch from master and call it `release/<major>.<minor>.<patch>`.
|
||||
1. Replace the usages of the old version in `README.md` with the new version (make sure to check the [Swift Package Manager](#swift-package-manager) and [CocoaPods](#cocoapods) sections).
|
||||
1. Bump the version in `OpenCombine.podspec`, `OpenCombineDispatch.podspec` and `OpenCombineFoundation.podspec`. In the latter two you will also need to set the `spec.dependency "OpenCombine"` property to the **previous** version. Why? Because otherwise the `pod lib lint` command that we run on our regular CI will fail when validating the `OpenCombineDispatch` and `OpenCombineFoundation` podspecs, since the dependencies are not yet in the trunk. If we set the dependencies to the previous version (which is already in the trunk), everything will be fine. This is purely to make the CI work. The clients will not experience any issues, since the version is specified as `>=`.
|
||||
1. Create a pull request to master for the release branch and make sure the CI passes.
|
||||
1. Merge the pull request.
|
||||
1. In the GitHub web interface on the [releases](https://github.com/OpenCombine/OpenCombine/releases) page, click the **Draft a new release** button.
|
||||
1. The **Tag version** and **Release title** fields should be filled with the version number.
|
||||
1. The description of the release should be consistent with the previous releases. It is a good practice to divide the description into several sections: additions, bugfixes, known issues etc. Also, be sure to mention the nicknames of the contributors of the new release.
|
||||
1. Publish the release.
|
||||
1. Switch to the master branch and pull the changes.
|
||||
1. Push the release to CocoaPods trunk. For that, execute the following commands:
|
||||
|
||||
```
|
||||
pod trunk push OpenCombine.podspec --verbose --allow-warnings
|
||||
pod trunk push OpenCombineDispatch.podspec --verbose --allow-warnings
|
||||
pod trunk push OpenCombineFoundation.podspec --verbose --allow-warnings
|
||||
```
|
||||
|
||||
Note that you need to be one of the owners of the pod for that.
|
||||
|
||||
#### GYB
|
||||
|
||||
@@ -85,3 +119,16 @@ GYB template files have the `.gyb` extension. Run `make gyb` to generate Swift c
|
||||
templates. The generated files are prefixed with `GENERATED-` and are checked into source control. Those
|
||||
files should never be edited directly. Instead, the `.gyb` template should be edited, and after that the files
|
||||
should be regenerated using `make gyb`.
|
||||
|
||||
#### Debugger Support
|
||||
|
||||
The file `opencombine_lldb.py` defines some `lldb` type summaries for easier debugging. These type summaries improve the way `lldb` and Xcode display some OpenCombine values.
|
||||
|
||||
To use `opencombine_lldb.py`, figure out its full path. Let's say the full path is `~/projects/OpenCombine/opencombine_lldb.py`. Then the following statement to your `~/.lldbinit` file:
|
||||
|
||||
command script import ~/projects/OpenCombine/opencombine_lldb.py
|
||||
|
||||
Currently, `opencombine_lldb.py` defines type summaries for these types:
|
||||
|
||||
- `Subscribers.Demand`
|
||||
- That's all for now.
|
||||
|
||||
+33
-1351
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
// From /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/swift/Foundation.swiftmodule/x86_64.swiftinterface
|
||||
// swift-interface-format-version: 1.0
|
||||
// swift-compiler-version: Apple Swift version 5.1.1 (swiftlang-1100.8.275.1 clang-1100.0.32.1)
|
||||
// swift-module-flags: -target x86_64-apple-macosx10.15 -enable-objc-interop -autolink-force-load -enable-library-evolution -module-link-name swiftFoundation -swift-version 5 -O -enforce-exclusivity=unchecked -module-name Foundation
|
||||
|
||||
public typealias Published = Combine.Published
|
||||
|
||||
public typealias ObservableObject = Combine.ObservableObject
|
||||
|
||||
public protocol _KeyValueCodingAndObservingPublishing {
|
||||
}
|
||||
|
||||
extension NSObject : Foundation._KeyValueCodingAndObservingPublishing {
|
||||
}
|
||||
|
||||
extension _KeyValueCodingAndObservingPublishing where Self : ObjectiveC.NSObject {
|
||||
public func publisher<Value>(for keyPath: Swift.KeyPath<Self, Value>, options: Foundation.NSKeyValueObservingOptions = [.initial, .new]) -> ObjectiveC.NSObject.KeyValueObservingPublisher<Self, Value>
|
||||
}
|
||||
|
||||
extension NSObject.KeyValueObservingPublisher {
|
||||
public func didChange() -> Combine.Publishers.Map<ObjectiveC.NSObject.KeyValueObservingPublisher<Subject, Value>, Swift.Void>
|
||||
}
|
||||
|
||||
extension NSObject {
|
||||
public struct KeyValueObservingPublisher<Subject, Value> : Swift.Equatable where Subject : ObjectiveC.NSObject {
|
||||
public let object: Subject
|
||||
public let keyPath: Swift.KeyPath<Subject, Value>
|
||||
public let options: Foundation.NSKeyValueObservingOptions
|
||||
public init(object: Subject, keyPath: Swift.KeyPath<Subject, Value>, options: Foundation.NSKeyValueObservingOptions)
|
||||
public static func == (lhs: ObjectiveC.NSObject.KeyValueObservingPublisher<Subject, Value>, rhs: ObjectiveC.NSObject.KeyValueObservingPublisher<Subject, Value>) -> Swift.Bool
|
||||
}
|
||||
}
|
||||
|
||||
extension NSObject.KeyValueObservingPublisher : Combine.Publisher {
|
||||
public typealias Output = Value
|
||||
public typealias Failure = Swift.Never
|
||||
public func receive<S>(subscriber: S) where Value == S.Input, S : Combine.Subscriber, S.Failure == ObjectiveC.NSObject.KeyValueObservingPublisher<Subject, Value>.Failure
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// BackDeployment.cpp
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 31.10.2019.
|
||||
//
|
||||
|
||||
// The content of this file is based on
|
||||
// https://github.com/apple/swift/blob/master/stdlib/public/runtime/BackDeployment.cpp
|
||||
// and must be updated accordingly.
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include "swift/Runtime/Config.h"
|
||||
|
||||
/// Returns true if the current OS version, at runtime, is a back-deployment
|
||||
/// version.
|
||||
static bool isBackDeploying() {
|
||||
if (__builtin_available(macOS 10.14.4, watchOS 5.2.0, iOS 12.2.0, tvOS 12.2.0, *)) {
|
||||
return false;
|
||||
} else {
|
||||
// We're in a pre-ABI-stable world
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long long computeIsSwiftMask() {
|
||||
return isBackDeploying() ? 1ULL : 2ULL;
|
||||
}
|
||||
|
||||
namespace opencombine {
|
||||
unsigned long long classIsSwiftMask = computeIsSwiftMask();
|
||||
}
|
||||
|
||||
#endif
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Locking.cpp
|
||||
// COpenCombineHelpers.cpp
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 23/09/2019.
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <cstdlib>
|
||||
#include <system_error>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <os/lock.h>
|
||||
@@ -235,4 +236,8 @@ void opencombine_unfair_recursive_lock_dealloc(OpenCombineUnfairRecursiveLock lo
|
||||
return delete static_cast<PlatformIndependentMutex*>(lock.opaque);
|
||||
}
|
||||
|
||||
void opencombine_stop_in_debugger(void) {
|
||||
raise(SIGTRAP);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -1,42 +0,0 @@
|
||||
//===--- Demangler.cpp - String to Node-Tree Demangling -------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements new Swift de-mangler.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - Some functions have been removed.
|
||||
// - The swift namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#include "swift/Demangling/Demangle.h"
|
||||
|
||||
using namespace opencombine;
|
||||
using namespace swift;
|
||||
|
||||
string_view
|
||||
swift::Demangle::makeSymbolicMangledNameStringRef(const char *base) {
|
||||
if (!base)
|
||||
return {};
|
||||
|
||||
auto end = base;
|
||||
while (*end != '\0') {
|
||||
// Skip over symbolic references.
|
||||
if (*end >= '\x01' && *end <= '\x17')
|
||||
end += sizeof(uint32_t);
|
||||
else if (*end >= '\x18' && *end <= '\x1F')
|
||||
end += sizeof(void*);
|
||||
++end;
|
||||
}
|
||||
return { base, size_t(end - base) };
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
//
|
||||
// EnumerateFields.cpp
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 25.10.2019.
|
||||
//
|
||||
|
||||
#include "COpenCombineHelpers.h"
|
||||
#include "swift/ABI/Metadata.h"
|
||||
#include "swift/Runtime/Metadata.h"
|
||||
#include "swift/Reflection/Records.h"
|
||||
#include "stl_polyfill/string_view.h"
|
||||
|
||||
using namespace opencombine;
|
||||
using namespace swift;
|
||||
using namespace reflection;
|
||||
|
||||
// This function is defined in the Swift runtime.
|
||||
OPENCOMBINE_SWIFT_CALLING_CONVENTION
|
||||
extern "C"
|
||||
const Metadata *
|
||||
swift_getTypeByMangledNameInContext(const char* typeNameStart,
|
||||
size_t typeNameLength,
|
||||
const ContextDescriptor* context,
|
||||
const Metadata* const* genericArgs);
|
||||
|
||||
namespace {
|
||||
const Metadata* getTypeMetadata(const FieldRecord& record,
|
||||
const Metadata* fieldOwner) {
|
||||
string_view mangledTypeName = record.getMangledTypeName(0);
|
||||
return swift_getTypeByMangledNameInContext(mangledTypeName.data(),
|
||||
mangledTypeName.size(),
|
||||
fieldOwner->getTypeContextDescriptor(),
|
||||
fieldOwner->getGenericArgs());
|
||||
}
|
||||
|
||||
string_view nextTupleLabel(const char*& labels) {
|
||||
const char* start = labels;
|
||||
while (true) {
|
||||
char current = *labels++;
|
||||
if (current == ' ' || current == '\0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { start, size_t(labels - start - 1) };
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
bool opencombine_enumerate_fields(const void* opaqueMetadataPtr,
|
||||
bool allowResilientSuperclasses,
|
||||
void* enumeratorContext,
|
||||
OpenCombineFieldEnumerator enumerator) {
|
||||
|
||||
auto enumerateFields = [&](const auto* metadata,
|
||||
const TypeContextDescriptor* description) -> bool {
|
||||
const auto* fieldOffsets = metadata->getFieldOffsets();
|
||||
const FieldDescriptor& fieldDescriptor = *description->Fields;
|
||||
|
||||
for (const FieldRecord& fieldRecord : fieldDescriptor) {
|
||||
if (!enumerator(enumeratorContext,
|
||||
fieldRecord.getFieldName(0).data(),
|
||||
*fieldOffsets++,
|
||||
getTypeMetadata(fieldRecord, metadata))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const Metadata* metadata = static_cast<const Metadata*>(opaqueMetadataPtr);
|
||||
|
||||
if (metadata->isClassObject()) {
|
||||
auto anyClassMetadata = static_cast<const AnyClassMetadata*>(metadata);
|
||||
if (!anyClassMetadata->isTypeMetadata()) {
|
||||
return true;
|
||||
}
|
||||
auto classMetadata = static_cast<const ClassMetadata*>(anyClassMetadata);
|
||||
|
||||
const ClassDescriptor* description = classMetadata->getDescription();
|
||||
|
||||
if (!allowResilientSuperclasses && description->hasResilientSuperclass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto superclassMetadata = classMetadata->Superclass) {
|
||||
if (!opencombine_enumerate_fields(superclassMetadata,
|
||||
allowResilientSuperclasses,
|
||||
enumeratorContext,
|
||||
enumerator)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return enumerateFields(classMetadata, description);
|
||||
}
|
||||
|
||||
if (const auto* structMetadata = llvm::dyn_cast<StructMetadata>(metadata)) {
|
||||
return enumerateFields(structMetadata, structMetadata->getDescription());
|
||||
}
|
||||
|
||||
if (const auto* tupleMetadata = llvm::dyn_cast<TupleTypeMetadata>(metadata)) {
|
||||
const char* labels = tupleMetadata->Labels;
|
||||
for (TupleTypeMetadata::StoredSize i = 0; i < tupleMetadata->NumElements; ++i) {
|
||||
const TupleTypeMetadata::Element& element = tupleMetadata->getElement(i);
|
||||
string_view nextLabel = nextTupleLabel(labels);
|
||||
std::string label(nextLabel.data(), nextLabel.size());
|
||||
if (!enumerator(enumeratorContext,
|
||||
label.c_str(),
|
||||
element.Offset,
|
||||
element.Type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
//===--- Metadata.cpp - Swift Language ABI Metadata Support ---------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Implementations of the metadata ABI functions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - Some functions have been removed.
|
||||
// - The swift namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#include "swift/Runtime/Metadata.h"
|
||||
|
||||
using namespace opencombine;
|
||||
using namespace swift;
|
||||
|
||||
#if OPENCOMBINE_SWIFT_OBJC_INTEROP
|
||||
static ClassMetadataBounds computeMetadataBoundsForObjCClass(Class cls) {
|
||||
cls = swift_getInitializedObjCClass(cls);
|
||||
auto metadata = reinterpret_cast<const ClassMetadata *>(cls);
|
||||
return metadata->getClassBoundsAsSwiftSuperclass();
|
||||
}
|
||||
#endif
|
||||
|
||||
static ClassMetadataBounds
|
||||
computeMetadataBoundsForSuperclass(const void *ref,
|
||||
TypeReferenceKind refKind) {
|
||||
switch (refKind) {
|
||||
case TypeReferenceKind::IndirectTypeDescriptor: {
|
||||
auto description = *reinterpret_cast<const ClassDescriptor * const *>(ref);
|
||||
if (!description) {
|
||||
// swift::fatalError(0, "instantiating class metadata for class with "
|
||||
// "missing weak-linked ancestor");
|
||||
abort();
|
||||
}
|
||||
return description->getMetadataBounds();
|
||||
}
|
||||
|
||||
case TypeReferenceKind::DirectTypeDescriptor: {
|
||||
auto description = reinterpret_cast<const ClassDescriptor *>(ref);
|
||||
return description->getMetadataBounds();
|
||||
}
|
||||
|
||||
case TypeReferenceKind::DirectObjCClassName: {
|
||||
#if OPENCOMBINE_SWIFT_OBJC_INTEROP
|
||||
auto cls = objc_lookUpClass(reinterpret_cast<const char *>(ref));
|
||||
return computeMetadataBoundsForObjCClass(cls);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
case TypeReferenceKind::IndirectObjCClass: {
|
||||
#if OPENCOMBINE_SWIFT_OBJC_INTEROP
|
||||
auto cls = *reinterpret_cast<const Class *>(ref);
|
||||
return computeMetadataBoundsForObjCClass(cls);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
opencombine_swift_runtime_unreachable("unsupported superclass reference kind");
|
||||
}
|
||||
|
||||
static ClassMetadataBounds computeMetadataBoundsFromSuperclass(
|
||||
const ClassDescriptor *description,
|
||||
StoredClassMetadataBounds &storedBounds) {
|
||||
ClassMetadataBounds bounds;
|
||||
|
||||
// Compute the bounds for the superclass, extending it to the minimum
|
||||
// bounds of a Swift class.
|
||||
if (const void *superRef = description->getResilientSuperclass()) {
|
||||
bounds = computeMetadataBoundsForSuperclass(superRef,
|
||||
description->getResilientSuperclassReferenceKind());
|
||||
} else {
|
||||
bounds = ClassMetadataBounds::forSwiftRootClass();
|
||||
}
|
||||
|
||||
// Add the subclass's immediate members.
|
||||
bounds.adjustForSubclass(description->areImmediateMembersNegative(),
|
||||
description->NumImmediateMembers);
|
||||
|
||||
// Cache before returning.
|
||||
storedBounds.initialize(bounds);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
ClassMetadataBounds
|
||||
swift::getResilientMetadataBounds(const ClassDescriptor *description) {
|
||||
assert(description->hasResilientSuperclass());
|
||||
auto &storedBounds = *description->ResilientMetadataBounds.get();
|
||||
|
||||
ClassMetadataBounds bounds;
|
||||
if (storedBounds.tryGet(bounds)) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
return computeMetadataBoundsFromSuperclass(description, storedBounds);
|
||||
}
|
||||
|
||||
int32_t
|
||||
swift::getResilientImmediateMembersOffset(const ClassDescriptor *description) {
|
||||
assert(description->hasResilientSuperclass());
|
||||
auto &storedBounds = *description->ResilientMetadataBounds.get();
|
||||
|
||||
ptrdiff_t result;
|
||||
if (storedBounds.tryGetImmediateMembersOffset(result)) {
|
||||
return result / sizeof(void*);
|
||||
}
|
||||
|
||||
auto bounds = computeMetadataBoundsFromSuperclass(description, storedBounds);
|
||||
return bounds.ImmediateMembersOffset / sizeof(void*);
|
||||
}
|
||||
@@ -9,8 +9,6 @@
|
||||
#define COPENCOMBINEHELPERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if __has_attribute(swift_name)
|
||||
# define OPENCOMBINE_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
|
||||
@@ -25,67 +23,54 @@ extern "C" {
|
||||
#pragma mark - CombineIdentifier
|
||||
|
||||
uint64_t opencombine_next_combine_identifier(void)
|
||||
OPENCOMBINE_SWIFT_NAME(nextCombineIdentifier());
|
||||
OPENCOMBINE_SWIFT_NAME(__nextCombineIdentifier());
|
||||
|
||||
#pragma mark - OpenCombineUnfairLock
|
||||
|
||||
/// A wrapper around an opaque pointer for type safety in Swift.
|
||||
typedef struct OpenCombineUnfairLock {
|
||||
void* _Nonnull opaque;
|
||||
} OPENCOMBINE_SWIFT_NAME(UnfairLock) OpenCombineUnfairLock;
|
||||
} OPENCOMBINE_SWIFT_NAME(__UnfairLock) OpenCombineUnfairLock;
|
||||
|
||||
/// Allocates a lock object. The allocated object must be destroyed by calling
|
||||
/// the destroy() method.
|
||||
OpenCombineUnfairLock opencombine_unfair_lock_alloc(void)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairLock.allocate());
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairLock.allocate());
|
||||
|
||||
void opencombine_unfair_lock_lock(OpenCombineUnfairLock)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairLock.lock(self:));
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairLock.lock(self:));
|
||||
|
||||
void opencombine_unfair_lock_unlock(OpenCombineUnfairLock)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairLock.unlock(self:));
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairLock.unlock(self:));
|
||||
|
||||
void opencombine_unfair_lock_assert_owner(OpenCombineUnfairLock mutex)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairLock.assertOwner(self:));
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairLock.assertOwner(self:));
|
||||
|
||||
void opencombine_unfair_lock_dealloc(OpenCombineUnfairLock lock)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairLock.deallocate(self:));
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairLock.deallocate(self:));
|
||||
|
||||
#pragma mark - OpenCombineUnfairRecursiveLock
|
||||
|
||||
/// A wrapper around an opaque pointer for type safety in Swift.
|
||||
typedef struct OpenCombineUnfairRecursiveLock {
|
||||
void* _Nonnull opaque;
|
||||
} OPENCOMBINE_SWIFT_NAME(UnfairRecursiveLock) OpenCombineUnfairRecursiveLock;
|
||||
} OPENCOMBINE_SWIFT_NAME(__UnfairRecursiveLock) OpenCombineUnfairRecursiveLock;
|
||||
|
||||
OpenCombineUnfairRecursiveLock opencombine_unfair_recursive_lock_alloc(void)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairRecursiveLock.allocate());
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairRecursiveLock.allocate());
|
||||
|
||||
void opencombine_unfair_recursive_lock_lock(OpenCombineUnfairRecursiveLock)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairRecursiveLock.lock(self:));
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairRecursiveLock.lock(self:));
|
||||
|
||||
void opencombine_unfair_recursive_lock_unlock(OpenCombineUnfairRecursiveLock)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairRecursiveLock.unlock(self:));
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairRecursiveLock.unlock(self:));
|
||||
|
||||
void opencombine_unfair_recursive_lock_dealloc(OpenCombineUnfairRecursiveLock lock)
|
||||
OPENCOMBINE_SWIFT_NAME(UnfairRecursiveLock.deallocate(self:));
|
||||
OPENCOMBINE_SWIFT_NAME(__UnfairRecursiveLock.deallocate(self:));
|
||||
|
||||
#pragma mark - Type metadata
|
||||
#pragma mark - Breakpoint
|
||||
|
||||
typedef bool(*_Nonnull OpenCombineFieldEnumerator)(
|
||||
void* _Nullable enumeratorContext,
|
||||
const char* _Nonnull fieldName,
|
||||
size_t fieldOffset,
|
||||
const void* _Nonnull fieldTypeMetadataPtr
|
||||
);
|
||||
|
||||
bool
|
||||
opencombine_enumerate_fields(
|
||||
const void* _Nonnull type_metadata,
|
||||
bool allowResilientSuperclasses,
|
||||
void* _Nullable enumerator_context,
|
||||
OpenCombineFieldEnumerator enumerator
|
||||
) OPENCOMBINE_SWIFT_NAME(enumerateFields(typeMetadata:allowResilientSuperclasses:enumeratorContext:enumerator:));
|
||||
void opencombine_stop_in_debugger(void) OPENCOMBINE_SWIFT_NAME(__stopInDebugger());
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
//===-- None.h - Simple null value for implicit construction ------*- C++ -*-=//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides None, an enumerator for use in implicit constructors
|
||||
// of various (usually templated) types to make such construction more
|
||||
// terse.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_ADT_NONE_H
|
||||
#define OPENCOMBINE_LLVM_ADT_NONE_H
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
/// A simple null object to allow implicit construction of Optional<T>
|
||||
/// and similar types without having to spell out the specialization's name.
|
||||
// (constant value 1 in an attempt to workaround MSVC build issue... )
|
||||
enum class NoneType { None = 1 };
|
||||
const NoneType None = NoneType::None;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,435 +0,0 @@
|
||||
//===- Optional.h - Simple variant for passing optional values --*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides Optional, a template class modeled in the spirit of
|
||||
// OCaml's 'opt' variant. The idea is to strongly type whether or not
|
||||
// a value can be optional.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_ADT_OPTIONAL_H
|
||||
#define OPENCOMBINE_LLVM_ADT_OPTIONAL_H
|
||||
|
||||
#include "llvm/ADT/None.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/type_traits.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
|
||||
class raw_ostream;
|
||||
|
||||
namespace optional_detail {
|
||||
|
||||
struct in_place_t {};
|
||||
|
||||
/// Storage for any type.
|
||||
template <typename T, bool = is_trivially_copyable<T>::value>
|
||||
class OptionalStorage {
|
||||
union {
|
||||
char empty;
|
||||
T value;
|
||||
};
|
||||
bool hasVal;
|
||||
|
||||
public:
|
||||
~OptionalStorage() { reset(); }
|
||||
|
||||
OptionalStorage() noexcept : empty(), hasVal(false) {}
|
||||
|
||||
OptionalStorage(OptionalStorage const &other) : OptionalStorage() {
|
||||
if (other.hasValue()) {
|
||||
emplace(other.value);
|
||||
}
|
||||
}
|
||||
OptionalStorage(OptionalStorage &&other) : OptionalStorage() {
|
||||
if (other.hasValue()) {
|
||||
emplace(std::move(other.value));
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
explicit OptionalStorage(in_place_t, Args &&... args)
|
||||
: value(std::forward<Args>(args)...), hasVal(true) {}
|
||||
|
||||
void reset() noexcept {
|
||||
if (hasVal) {
|
||||
value.~T();
|
||||
hasVal = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasValue() const noexcept { return hasVal; }
|
||||
|
||||
T &getValue() LLVM_LVALUE_FUNCTION noexcept {
|
||||
assert(hasVal);
|
||||
return value;
|
||||
}
|
||||
T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
|
||||
assert(hasVal);
|
||||
return value;
|
||||
}
|
||||
#if LLVM_HAS_RVALUE_REFERENCE_THIS
|
||||
T &&getValue() && noexcept {
|
||||
assert(hasVal);
|
||||
return std::move(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class... Args> void emplace(Args &&... args) {
|
||||
reset();
|
||||
::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
|
||||
hasVal = true;
|
||||
}
|
||||
|
||||
OptionalStorage &operator=(T const &y) {
|
||||
if (hasValue()) {
|
||||
value = y;
|
||||
} else {
|
||||
::new ((void *)std::addressof(value)) T(y);
|
||||
hasVal = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
OptionalStorage &operator=(T &&y) {
|
||||
if (hasValue()) {
|
||||
value = std::move(y);
|
||||
} else {
|
||||
::new ((void *)std::addressof(value)) T(std::move(y));
|
||||
hasVal = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionalStorage &operator=(OptionalStorage const &other) {
|
||||
if (other.hasValue()) {
|
||||
if (hasValue()) {
|
||||
value = other.value;
|
||||
} else {
|
||||
::new ((void *)std::addressof(value)) T(other.value);
|
||||
hasVal = true;
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionalStorage &operator=(OptionalStorage &&other) {
|
||||
if (other.hasValue()) {
|
||||
if (hasValue()) {
|
||||
value = std::move(other.value);
|
||||
} else {
|
||||
::new ((void *)std::addressof(value)) T(std::move(other.value));
|
||||
hasVal = true;
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class OptionalStorage<T, true> {
|
||||
union {
|
||||
char empty;
|
||||
T value;
|
||||
};
|
||||
bool hasVal = false;
|
||||
|
||||
public:
|
||||
~OptionalStorage() = default;
|
||||
|
||||
OptionalStorage() noexcept : empty{} {}
|
||||
|
||||
OptionalStorage(OptionalStorage const &other) = default;
|
||||
OptionalStorage(OptionalStorage &&other) = default;
|
||||
|
||||
OptionalStorage &operator=(OptionalStorage const &other) = default;
|
||||
OptionalStorage &operator=(OptionalStorage &&other) = default;
|
||||
|
||||
template <class... Args>
|
||||
explicit OptionalStorage(in_place_t, Args &&... args)
|
||||
: value(std::forward<Args>(args)...), hasVal(true) {}
|
||||
|
||||
void reset() noexcept {
|
||||
if (hasVal) {
|
||||
value.~T();
|
||||
hasVal = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasValue() const noexcept { return hasVal; }
|
||||
|
||||
T &getValue() LLVM_LVALUE_FUNCTION noexcept {
|
||||
assert(hasVal);
|
||||
return value;
|
||||
}
|
||||
T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
|
||||
assert(hasVal);
|
||||
return value;
|
||||
}
|
||||
#if LLVM_HAS_RVALUE_REFERENCE_THIS
|
||||
T &&getValue() && noexcept {
|
||||
assert(hasVal);
|
||||
return std::move(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class... Args> void emplace(Args &&... args) {
|
||||
reset();
|
||||
::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
|
||||
hasVal = true;
|
||||
}
|
||||
|
||||
OptionalStorage &operator=(T const &y) {
|
||||
if (hasValue()) {
|
||||
value = y;
|
||||
} else {
|
||||
::new ((void *)std::addressof(value)) T(y);
|
||||
hasVal = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
OptionalStorage &operator=(T &&y) {
|
||||
if (hasValue()) {
|
||||
value = std::move(y);
|
||||
} else {
|
||||
::new ((void *)std::addressof(value)) T(std::move(y));
|
||||
hasVal = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace optional_detail
|
||||
|
||||
template <typename T> class Optional {
|
||||
optional_detail::OptionalStorage<T> Storage;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
constexpr Optional() {}
|
||||
constexpr Optional(NoneType) {}
|
||||
|
||||
Optional(const T &y) : Storage(optional_detail::in_place_t{}, y) {}
|
||||
Optional(const Optional &O) = default;
|
||||
|
||||
Optional(T &&y) : Storage(optional_detail::in_place_t{}, std::move(y)) {}
|
||||
Optional(Optional &&O) = default;
|
||||
|
||||
Optional &operator=(T &&y) {
|
||||
Storage = std::move(y);
|
||||
return *this;
|
||||
}
|
||||
Optional &operator=(Optional &&O) = default;
|
||||
|
||||
/// Create a new object by constructing it in place with the given arguments.
|
||||
template <typename... ArgTypes> void emplace(ArgTypes &&... Args) {
|
||||
Storage.emplace(std::forward<ArgTypes>(Args)...);
|
||||
}
|
||||
|
||||
static inline Optional create(const T *y) {
|
||||
return y ? Optional(*y) : Optional();
|
||||
}
|
||||
|
||||
Optional &operator=(const T &y) {
|
||||
Storage = y;
|
||||
return *this;
|
||||
}
|
||||
Optional &operator=(const Optional &O) = default;
|
||||
|
||||
void reset() { Storage.reset(); }
|
||||
|
||||
const T *getPointer() const { return &Storage.getValue(); }
|
||||
T *getPointer() { return &Storage.getValue(); }
|
||||
const T &getValue() const LLVM_LVALUE_FUNCTION { return Storage.getValue(); }
|
||||
T &getValue() LLVM_LVALUE_FUNCTION { return Storage.getValue(); }
|
||||
|
||||
explicit operator bool() const { return hasValue(); }
|
||||
bool hasValue() const { return Storage.hasValue(); }
|
||||
const T *operator->() const { return getPointer(); }
|
||||
T *operator->() { return getPointer(); }
|
||||
const T &operator*() const LLVM_LVALUE_FUNCTION { return getValue(); }
|
||||
T &operator*() LLVM_LVALUE_FUNCTION { return getValue(); }
|
||||
|
||||
template <typename U>
|
||||
constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION {
|
||||
return hasValue() ? getValue() : std::forward<U>(value);
|
||||
}
|
||||
|
||||
#if LLVM_HAS_RVALUE_REFERENCE_THIS
|
||||
T &&getValue() && { return std::move(Storage.getValue()); }
|
||||
T &&operator*() && { return std::move(Storage.getValue()); }
|
||||
|
||||
template <typename U>
|
||||
T getValueOr(U &&value) && {
|
||||
return hasValue() ? std::move(getValue()) : std::forward<U>(value);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const Optional<T> &X, const Optional<U> &Y) {
|
||||
if (X && Y)
|
||||
return *X == *Y;
|
||||
return X.hasValue() == Y.hasValue();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const Optional<T> &X, const Optional<U> &Y) {
|
||||
return !(X == Y);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator<(const Optional<T> &X, const Optional<U> &Y) {
|
||||
if (X && Y)
|
||||
return *X < *Y;
|
||||
return X.hasValue() < Y.hasValue();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator<=(const Optional<T> &X, const Optional<U> &Y) {
|
||||
return !(Y < X);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator>(const Optional<T> &X, const Optional<U> &Y) {
|
||||
return Y < X;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator>=(const Optional<T> &X, const Optional<U> &Y) {
|
||||
return !(X < Y);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator==(const Optional<T> &X, NoneType) {
|
||||
return !X;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator==(NoneType, const Optional<T> &X) {
|
||||
return X == None;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(const Optional<T> &X, NoneType) {
|
||||
return !(X == None);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(NoneType, const Optional<T> &X) {
|
||||
return X != None;
|
||||
}
|
||||
|
||||
template <typename T> bool operator<(const Optional<T> &X, NoneType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T> bool operator<(NoneType, const Optional<T> &X) {
|
||||
return X.hasValue();
|
||||
}
|
||||
|
||||
template <typename T> bool operator<=(const Optional<T> &X, NoneType) {
|
||||
return !(None < X);
|
||||
}
|
||||
|
||||
template <typename T> bool operator<=(NoneType, const Optional<T> &X) {
|
||||
return !(X < None);
|
||||
}
|
||||
|
||||
template <typename T> bool operator>(const Optional<T> &X, NoneType) {
|
||||
return None < X;
|
||||
}
|
||||
|
||||
template <typename T> bool operator>(NoneType, const Optional<T> &X) {
|
||||
return X < None;
|
||||
}
|
||||
|
||||
template <typename T> bool operator>=(const Optional<T> &X, NoneType) {
|
||||
return None <= X;
|
||||
}
|
||||
|
||||
template <typename T> bool operator>=(NoneType, const Optional<T> &X) {
|
||||
return X <= None;
|
||||
}
|
||||
|
||||
template <typename T> bool operator==(const Optional<T> &X, const T &Y) {
|
||||
return X && *X == Y;
|
||||
}
|
||||
|
||||
template <typename T> bool operator==(const T &X, const Optional<T> &Y) {
|
||||
return Y && X == *Y;
|
||||
}
|
||||
|
||||
template <typename T> bool operator!=(const Optional<T> &X, const T &Y) {
|
||||
return !(X == Y);
|
||||
}
|
||||
|
||||
template <typename T> bool operator!=(const T &X, const Optional<T> &Y) {
|
||||
return !(X == Y);
|
||||
}
|
||||
|
||||
template <typename T> bool operator<(const Optional<T> &X, const T &Y) {
|
||||
return !X || *X < Y;
|
||||
}
|
||||
|
||||
template <typename T> bool operator<(const T &X, const Optional<T> &Y) {
|
||||
return Y && X < *Y;
|
||||
}
|
||||
|
||||
template <typename T> bool operator<=(const Optional<T> &X, const T &Y) {
|
||||
return !(Y < X);
|
||||
}
|
||||
|
||||
template <typename T> bool operator<=(const T &X, const Optional<T> &Y) {
|
||||
return !(Y < X);
|
||||
}
|
||||
|
||||
template <typename T> bool operator>(const Optional<T> &X, const T &Y) {
|
||||
return Y < X;
|
||||
}
|
||||
|
||||
template <typename T> bool operator>(const T &X, const Optional<T> &Y) {
|
||||
return Y < X;
|
||||
}
|
||||
|
||||
template <typename T> bool operator>=(const Optional<T> &X, const T &Y) {
|
||||
return !(X < Y);
|
||||
}
|
||||
|
||||
template <typename T> bool operator>=(const T &X, const Optional<T> &Y) {
|
||||
return !(X < Y);
|
||||
}
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, NoneType);
|
||||
|
||||
template <typename T, typename = decltype(std::declval<raw_ostream &>()
|
||||
<< std::declval<const T &>())>
|
||||
raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) {
|
||||
if (O)
|
||||
OS << *O;
|
||||
else
|
||||
OS << None;
|
||||
return OS;
|
||||
}
|
||||
|
||||
} // end namespace llvm
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_LLVM_ADT_OPTIONAL_H
|
||||
@@ -1,61 +0,0 @@
|
||||
//===--- AlignOf.h - Portable calculation of type alignment -----*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the AlignedCharArrayUnion class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_SUPPORT_ALIGNOF_H
|
||||
#define OPENCOMBINE_LLVM_SUPPORT_ALIGNOF_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <cstddef>
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename... Ts> class AlignerImpl {
|
||||
T t;
|
||||
AlignerImpl<Ts...> rest;
|
||||
AlignerImpl() = delete;
|
||||
};
|
||||
|
||||
template <typename T> class AlignerImpl<T> {
|
||||
T t;
|
||||
AlignerImpl() = delete;
|
||||
};
|
||||
|
||||
template <typename T, typename... Ts> union SizerImpl {
|
||||
char arr[sizeof(T)];
|
||||
SizerImpl<Ts...> rest;
|
||||
};
|
||||
|
||||
template <typename T> union SizerImpl<T> { char arr[sizeof(T)]; };
|
||||
} // end namespace detail
|
||||
|
||||
/// A suitably aligned and sized character array member which can hold elements
|
||||
/// of any type.
|
||||
///
|
||||
/// These types may be arrays, structs, or any other types. This exposes a
|
||||
/// `buffer` member which can be used as suitable storage for a placement new of
|
||||
/// any of these types.
|
||||
template <typename T, typename... Ts> struct AlignedCharArrayUnion {
|
||||
alignas(detail::AlignerImpl<T, Ts...>) char buffer[sizeof(
|
||||
llvm::detail::SizerImpl<T, Ts...>)];
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_LLVM_SUPPORT_ALIGNOF_H
|
||||
@@ -1,409 +0,0 @@
|
||||
//===-- llvm/Support/Alignment.h - Useful alignment functions ---*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains types to represent alignments.
|
||||
// They are instrumented to guarantee some invariants are preserved and prevent
|
||||
// invalid manipulations.
|
||||
//
|
||||
// - Align represents an alignment in bytes, it is always set and always a valid
|
||||
// power of two, its minimum value is 1 which means no alignment requirements.
|
||||
//
|
||||
// - MaybeAlign is an optional type, it may be undefined or set. When it's set
|
||||
// you can get the underlying Align type by using the getValue() method.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_SUPPORT_ALIGNMENT_H_
|
||||
#define OPENCOMBINE_LLVM_SUPPORT_ALIGNMENT_H_
|
||||
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
|
||||
#define ALIGN_CHECK_ISPOSITIVE(decl) \
|
||||
assert(decl > 0 && (#decl " should be defined"))
|
||||
#define ALIGN_CHECK_ISSET(decl) \
|
||||
assert(decl.hasValue() && (#decl " should be defined"))
|
||||
|
||||
/// This struct is a compact representation of a valid (non-zero power of two)
|
||||
/// alignment.
|
||||
/// It is suitable for use as static global constants.
|
||||
struct Align {
|
||||
private:
|
||||
uint8_t ShiftValue = 0; /// The log2 of the required alignment.
|
||||
/// ShiftValue is less than 64 by construction.
|
||||
|
||||
friend struct MaybeAlign;
|
||||
friend unsigned Log2(Align);
|
||||
friend bool operator==(Align Lhs, Align Rhs);
|
||||
friend bool operator!=(Align Lhs, Align Rhs);
|
||||
friend bool operator<=(Align Lhs, Align Rhs);
|
||||
friend bool operator>=(Align Lhs, Align Rhs);
|
||||
friend bool operator<(Align Lhs, Align Rhs);
|
||||
friend bool operator>(Align Lhs, Align Rhs);
|
||||
friend unsigned encode(struct MaybeAlign A);
|
||||
friend struct MaybeAlign decodeMaybeAlign(unsigned Value);
|
||||
|
||||
/// A trivial type to allow construction of constexpr Align.
|
||||
/// This is currently needed to workaround a bug in GCC 5.3 which prevents
|
||||
/// definition of constexpr assign operators.
|
||||
/// https://stackoverflow.com/questions/46756288/explicitly-defaulted-function-cannot-be-declared-as-constexpr-because-the-implic
|
||||
/// FIXME: Remove this, make all assign operators constexpr and introduce user
|
||||
/// defined literals when we don't have to support GCC 5.3 anymore.
|
||||
/// https://llvm.org/docs/GettingStarted.html#getting-a-modern-host-c-toolchain
|
||||
struct LogValue {
|
||||
uint8_t Log;
|
||||
};
|
||||
|
||||
public:
|
||||
/// Default is byte-aligned.
|
||||
constexpr Align() = default;
|
||||
/// Do not perform checks in case of copy/move construct/assign, because the
|
||||
/// checks have been performed when building `Other`.
|
||||
constexpr Align(const Align &Other) = default;
|
||||
constexpr Align(Align &&Other) = default;
|
||||
Align &operator=(const Align &Other) = default;
|
||||
Align &operator=(Align &&Other) = default;
|
||||
|
||||
explicit Align(uint64_t Value) {
|
||||
assert(Value > 0 && "Value must not be 0");
|
||||
assert(llvm::isPowerOf2_64(Value) && "Alignment is not a power of 2");
|
||||
ShiftValue = Log2_64(Value);
|
||||
assert(ShiftValue < 64 && "Broken invariant");
|
||||
}
|
||||
|
||||
/// This is a hole in the type system and should not be abused.
|
||||
/// Needed to interact with C for instance.
|
||||
uint64_t value() const { return uint64_t(1) << ShiftValue; }
|
||||
|
||||
/// Returns a default constructed Align which corresponds to no alignment.
|
||||
/// This is useful to test for unalignment as it conveys clear semantic.
|
||||
/// `if (A != Align::None())`
|
||||
/// would be better than
|
||||
/// `if (A > Align(1))`
|
||||
constexpr static const Align None() { return Align(); }
|
||||
|
||||
/// Allow constructions of constexpr Align.
|
||||
template <size_t kValue> constexpr static LogValue Constant() {
|
||||
return LogValue{static_cast<uint8_t>(CTLog2<kValue>())};
|
||||
}
|
||||
|
||||
/// Allow constructions of constexpr Align from types.
|
||||
/// Compile time equivalent to Align(alignof(T)).
|
||||
template <typename T> constexpr static LogValue Of() {
|
||||
return Constant<std::alignment_of<T>::value>();
|
||||
}
|
||||
|
||||
/// Constexpr constructor from LogValue type.
|
||||
constexpr Align(LogValue CA) : ShiftValue(CA.Log) {}
|
||||
};
|
||||
|
||||
/// Treats the value 0 as a 1, so Align is always at least 1.
|
||||
inline Align assumeAligned(uint64_t Value) {
|
||||
return Value ? Align(Value) : Align();
|
||||
}
|
||||
|
||||
/// This struct is a compact representation of a valid (power of two) or
|
||||
/// undefined (0) alignment.
|
||||
struct MaybeAlign : public llvm::Optional<Align> {
|
||||
private:
|
||||
using UP = llvm::Optional<Align>;
|
||||
|
||||
public:
|
||||
/// Default is undefined.
|
||||
MaybeAlign() = default;
|
||||
/// Do not perform checks in case of copy/move construct/assign, because the
|
||||
/// checks have been performed when building `Other`.
|
||||
MaybeAlign(const MaybeAlign &Other) = default;
|
||||
MaybeAlign &operator=(const MaybeAlign &Other) = default;
|
||||
MaybeAlign(MaybeAlign &&Other) = default;
|
||||
MaybeAlign &operator=(MaybeAlign &&Other) = default;
|
||||
|
||||
/// Use llvm::Optional<Align> constructor.
|
||||
using UP::UP;
|
||||
|
||||
explicit MaybeAlign(uint64_t Value) {
|
||||
assert((Value == 0 || llvm::isPowerOf2_64(Value)) &&
|
||||
"Alignment is neither 0 nor a power of 2");
|
||||
if (Value)
|
||||
emplace(Value);
|
||||
}
|
||||
|
||||
/// For convenience, returns a valid alignment or 1 if undefined.
|
||||
Align valueOrOne() const { return hasValue() ? getValue() : Align(); }
|
||||
};
|
||||
|
||||
/// Checks that SizeInBytes is a multiple of the alignment.
|
||||
inline bool isAligned(Align Lhs, uint64_t SizeInBytes) {
|
||||
return SizeInBytes % Lhs.value() == 0;
|
||||
}
|
||||
|
||||
/// Checks that SizeInBytes is a multiple of the alignment.
|
||||
/// Returns false if the alignment is undefined.
|
||||
inline bool isAligned(MaybeAlign Lhs, uint64_t SizeInBytes) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
return SizeInBytes % (*Lhs).value() == 0;
|
||||
}
|
||||
|
||||
/// Checks that Addr is a multiple of the alignment.
|
||||
inline bool isAddrAligned(Align Lhs, const void *Addr) {
|
||||
return isAligned(Lhs, reinterpret_cast<uintptr_t>(Addr));
|
||||
}
|
||||
|
||||
/// Returns a multiple of A needed to store `Size` bytes.
|
||||
inline uint64_t alignTo(uint64_t Size, Align A) {
|
||||
const uint64_t value = A.value();
|
||||
// The following line is equivalent to `(Size + value - 1) / value * value`.
|
||||
|
||||
// The division followed by a multiplication can be thought of as a right
|
||||
// shift followed by a left shift which zeros out the extra bits produced in
|
||||
// the bump; `~(value - 1)` is a mask where all those bits being zeroed out
|
||||
// are just zero.
|
||||
|
||||
// Most compilers can generate this code but the pattern may be missed when
|
||||
// multiple functions gets inlined.
|
||||
return (Size + value - 1) & ~(value - 1);
|
||||
}
|
||||
|
||||
/// Returns a multiple of A needed to store `Size` bytes.
|
||||
/// Returns `Size` if current alignment is undefined.
|
||||
inline uint64_t alignTo(uint64_t Size, MaybeAlign A) {
|
||||
return A ? alignTo(Size, A.getValue()) : Size;
|
||||
}
|
||||
|
||||
/// Aligns `Addr` to `Alignment` bytes, rounding up.
|
||||
inline uintptr_t alignAddr(const void *Addr, Align Alignment) {
|
||||
uintptr_t ArithAddr = reinterpret_cast<uintptr_t>(Addr);
|
||||
assert(static_cast<uintptr_t>(ArithAddr + Alignment.value() - 1) >=
|
||||
ArithAddr && "Overflow");
|
||||
return alignTo(ArithAddr, Alignment);
|
||||
}
|
||||
|
||||
/// Returns the offset to the next integer (mod 2**64) that is greater than
|
||||
/// or equal to \p Value and is a multiple of \p Align.
|
||||
inline uint64_t offsetToAlignment(uint64_t Value, Align Alignment) {
|
||||
return alignTo(Value, Alignment) - Value;
|
||||
}
|
||||
|
||||
/// Returns the necessary adjustment for aligning `Addr` to `Alignment`
|
||||
/// bytes, rounding up.
|
||||
inline uint64_t offsetToAlignedAddr(const void *Addr, Align Alignment) {
|
||||
return offsetToAlignment(reinterpret_cast<uintptr_t>(Addr), Alignment);
|
||||
}
|
||||
|
||||
/// Returns the log2 of the alignment.
|
||||
inline unsigned Log2(Align A) { return A.ShiftValue; }
|
||||
|
||||
/// Returns the log2 of the alignment.
|
||||
/// \pre A must be defined.
|
||||
inline unsigned Log2(MaybeAlign A) {
|
||||
ALIGN_CHECK_ISSET(A);
|
||||
return Log2(A.getValue());
|
||||
}
|
||||
|
||||
/// Returns the alignment that satisfies both alignments.
|
||||
/// Same semantic as MinAlign.
|
||||
inline Align commonAlignment(Align A, Align B) { return std::min(A, B); }
|
||||
|
||||
/// Returns the alignment that satisfies both alignments.
|
||||
/// Same semantic as MinAlign.
|
||||
inline Align commonAlignment(Align A, uint64_t Offset) {
|
||||
return Align(MinAlign(A.value(), Offset));
|
||||
}
|
||||
|
||||
/// Returns the alignment that satisfies both alignments.
|
||||
/// Same semantic as MinAlign.
|
||||
inline MaybeAlign commonAlignment(MaybeAlign A, MaybeAlign B) {
|
||||
return A && B ? commonAlignment(*A, *B) : A ? A : B;
|
||||
}
|
||||
|
||||
/// Returns the alignment that satisfies both alignments.
|
||||
/// Same semantic as MinAlign.
|
||||
inline MaybeAlign commonAlignment(MaybeAlign A, uint64_t Offset) {
|
||||
return MaybeAlign(MinAlign((*A).value(), Offset));
|
||||
}
|
||||
|
||||
/// Returns a representation of the alignment that encodes undefined as 0.
|
||||
inline unsigned encode(MaybeAlign A) { return A ? A->ShiftValue + 1 : 0; }
|
||||
|
||||
/// Dual operation of the encode function above.
|
||||
inline MaybeAlign decodeMaybeAlign(unsigned Value) {
|
||||
if (Value == 0)
|
||||
return MaybeAlign();
|
||||
Align Out;
|
||||
Out.ShiftValue = Value - 1;
|
||||
return Out;
|
||||
}
|
||||
|
||||
/// Returns a representation of the alignment, the encoded value is positive by
|
||||
/// definition.
|
||||
inline unsigned encode(Align A) { return encode(MaybeAlign(A)); }
|
||||
|
||||
/// Comparisons between Align and scalars. Rhs must be positive.
|
||||
inline bool operator==(Align Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return Lhs.value() == Rhs;
|
||||
}
|
||||
inline bool operator!=(Align Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return Lhs.value() != Rhs;
|
||||
}
|
||||
inline bool operator<=(Align Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return Lhs.value() <= Rhs;
|
||||
}
|
||||
inline bool operator>=(Align Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return Lhs.value() >= Rhs;
|
||||
}
|
||||
inline bool operator<(Align Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return Lhs.value() < Rhs;
|
||||
}
|
||||
inline bool operator>(Align Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return Lhs.value() > Rhs;
|
||||
}
|
||||
|
||||
/// Comparisons between MaybeAlign and scalars.
|
||||
inline bool operator==(MaybeAlign Lhs, uint64_t Rhs) {
|
||||
return Lhs ? (*Lhs).value() == Rhs : Rhs == 0;
|
||||
}
|
||||
inline bool operator!=(MaybeAlign Lhs, uint64_t Rhs) {
|
||||
return Lhs ? (*Lhs).value() != Rhs : Rhs != 0;
|
||||
}
|
||||
inline bool operator<=(MaybeAlign Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return (*Lhs).value() <= Rhs;
|
||||
}
|
||||
inline bool operator>=(MaybeAlign Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return (*Lhs).value() >= Rhs;
|
||||
}
|
||||
inline bool operator<(MaybeAlign Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return (*Lhs).value() < Rhs;
|
||||
}
|
||||
inline bool operator>(MaybeAlign Lhs, uint64_t Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
ALIGN_CHECK_ISPOSITIVE(Rhs);
|
||||
return (*Lhs).value() > Rhs;
|
||||
}
|
||||
|
||||
/// Comparisons operators between Align.
|
||||
inline bool operator==(Align Lhs, Align Rhs) {
|
||||
return Lhs.ShiftValue == Rhs.ShiftValue;
|
||||
}
|
||||
inline bool operator!=(Align Lhs, Align Rhs) {
|
||||
return Lhs.ShiftValue != Rhs.ShiftValue;
|
||||
}
|
||||
inline bool operator<=(Align Lhs, Align Rhs) {
|
||||
return Lhs.ShiftValue <= Rhs.ShiftValue;
|
||||
}
|
||||
inline bool operator>=(Align Lhs, Align Rhs) {
|
||||
return Lhs.ShiftValue >= Rhs.ShiftValue;
|
||||
}
|
||||
inline bool operator<(Align Lhs, Align Rhs) {
|
||||
return Lhs.ShiftValue < Rhs.ShiftValue;
|
||||
}
|
||||
inline bool operator>(Align Lhs, Align Rhs) {
|
||||
return Lhs.ShiftValue > Rhs.ShiftValue;
|
||||
}
|
||||
|
||||
/// Comparisons operators between Align and MaybeAlign.
|
||||
inline bool operator==(Align Lhs, MaybeAlign Rhs) {
|
||||
ALIGN_CHECK_ISSET(Rhs);
|
||||
return Lhs.value() == (*Rhs).value();
|
||||
}
|
||||
inline bool operator!=(Align Lhs, MaybeAlign Rhs) {
|
||||
ALIGN_CHECK_ISSET(Rhs);
|
||||
return Lhs.value() != (*Rhs).value();
|
||||
}
|
||||
inline bool operator<=(Align Lhs, MaybeAlign Rhs) {
|
||||
ALIGN_CHECK_ISSET(Rhs);
|
||||
return Lhs.value() <= (*Rhs).value();
|
||||
}
|
||||
inline bool operator>=(Align Lhs, MaybeAlign Rhs) {
|
||||
ALIGN_CHECK_ISSET(Rhs);
|
||||
return Lhs.value() >= (*Rhs).value();
|
||||
}
|
||||
inline bool operator<(Align Lhs, MaybeAlign Rhs) {
|
||||
ALIGN_CHECK_ISSET(Rhs);
|
||||
return Lhs.value() < (*Rhs).value();
|
||||
}
|
||||
inline bool operator>(Align Lhs, MaybeAlign Rhs) {
|
||||
ALIGN_CHECK_ISSET(Rhs);
|
||||
return Lhs.value() > (*Rhs).value();
|
||||
}
|
||||
|
||||
/// Comparisons operators between MaybeAlign and Align.
|
||||
inline bool operator==(MaybeAlign Lhs, Align Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
return Lhs && (*Lhs).value() == Rhs.value();
|
||||
}
|
||||
inline bool operator!=(MaybeAlign Lhs, Align Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
return Lhs && (*Lhs).value() != Rhs.value();
|
||||
}
|
||||
inline bool operator<=(MaybeAlign Lhs, Align Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
return Lhs && (*Lhs).value() <= Rhs.value();
|
||||
}
|
||||
inline bool operator>=(MaybeAlign Lhs, Align Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
return Lhs && (*Lhs).value() >= Rhs.value();
|
||||
}
|
||||
inline bool operator<(MaybeAlign Lhs, Align Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
return Lhs && (*Lhs).value() < Rhs.value();
|
||||
}
|
||||
inline bool operator>(MaybeAlign Lhs, Align Rhs) {
|
||||
ALIGN_CHECK_ISSET(Lhs);
|
||||
return Lhs && (*Lhs).value() > Rhs.value();
|
||||
}
|
||||
|
||||
inline Align operator/(Align Lhs, uint64_t Divisor) {
|
||||
assert(llvm::isPowerOf2_64(Divisor) &&
|
||||
"Divisor must be positive and a power of 2");
|
||||
assert(Lhs != 1 && "Can't halve byte alignment");
|
||||
return Align(Lhs.value() / Divisor);
|
||||
}
|
||||
|
||||
inline MaybeAlign operator/(MaybeAlign Lhs, uint64_t Divisor) {
|
||||
assert(llvm::isPowerOf2_64(Divisor) &&
|
||||
"Divisor must be positive and a power of 2");
|
||||
return Lhs ? Lhs.getValue() / Divisor : MaybeAlign();
|
||||
}
|
||||
|
||||
inline Align max(MaybeAlign Lhs, Align Rhs) {
|
||||
return Lhs && *Lhs > Rhs ? *Lhs : Rhs;
|
||||
}
|
||||
|
||||
inline Align max(Align Lhs, MaybeAlign Rhs) {
|
||||
return Rhs && *Rhs > Lhs ? *Rhs : Lhs;
|
||||
}
|
||||
|
||||
#undef ALIGN_CHECK_ISPOSITIVE
|
||||
#undef ALIGN_CHECK_ISSET
|
||||
|
||||
} // namespace llvm
|
||||
} // namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_LLVM_SUPPORT_ALIGNMENT_H_
|
||||
@@ -1,414 +0,0 @@
|
||||
//===- llvm/Support/Casting.h - Allow flexible, checked, casts --*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the isa<X>(), cast<X>(), dyn_cast<X>(), cast_or_null<X>(),
|
||||
// and dyn_cast_or_null<X>() templates.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_SUPPORT_CASTING_H
|
||||
#define OPENCOMBINE_LLVM_SUPPORT_CASTING_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/type_traits.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// isa<x> Support Templates
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Define a template that can be specialized by smart pointers to reflect the
|
||||
// fact that they are automatically dereferenced, and are not involved with the
|
||||
// template selection process... the default implementation is a noop.
|
||||
//
|
||||
template<typename From> struct simplify_type {
|
||||
using SimpleType = From; // The real type this represents...
|
||||
|
||||
// An accessor to get the real value...
|
||||
static SimpleType &getSimplifiedValue(From &Val) { return Val; }
|
||||
};
|
||||
|
||||
template<typename From> struct simplify_type<const From> {
|
||||
using NonConstSimpleType = typename simplify_type<From>::SimpleType;
|
||||
using SimpleType =
|
||||
typename add_const_past_pointer<NonConstSimpleType>::type;
|
||||
using RetType =
|
||||
typename add_lvalue_reference_if_not_pointer<SimpleType>::type;
|
||||
|
||||
static RetType getSimplifiedValue(const From& Val) {
|
||||
return simplify_type<From>::getSimplifiedValue(const_cast<From&>(Val));
|
||||
}
|
||||
};
|
||||
|
||||
// The core of the implementation of isa<X> is here; To and From should be
|
||||
// the names of classes. This template can be specialized to customize the
|
||||
// implementation of isa<> without rewriting it from scratch.
|
||||
template <typename To, typename From, typename Enabler = void>
|
||||
struct isa_impl {
|
||||
static inline bool doit(const From &Val) {
|
||||
return To::classof(&Val);
|
||||
}
|
||||
};
|
||||
|
||||
/// Always allow upcasts, and perform no dynamic check for them.
|
||||
template <typename To, typename From>
|
||||
struct isa_impl<
|
||||
To, From, typename std::enable_if<std::is_base_of<To, From>::value>::type> {
|
||||
static inline bool doit(const From &) { return true; }
|
||||
};
|
||||
|
||||
template <typename To, typename From> struct isa_impl_cl {
|
||||
static inline bool doit(const From &Val) {
|
||||
return isa_impl<To, From>::doit(Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename To, typename From> struct isa_impl_cl<To, const From> {
|
||||
static inline bool doit(const From &Val) {
|
||||
return isa_impl<To, From>::doit(Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename To, typename From>
|
||||
struct isa_impl_cl<To, const std::unique_ptr<From>> {
|
||||
static inline bool doit(const std::unique_ptr<From> &Val) {
|
||||
assert(Val && "isa<> used on a null pointer");
|
||||
return isa_impl_cl<To, From>::doit(*Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename To, typename From> struct isa_impl_cl<To, From*> {
|
||||
static inline bool doit(const From *Val) {
|
||||
assert(Val && "isa<> used on a null pointer");
|
||||
return isa_impl<To, From>::doit(*Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename To, typename From> struct isa_impl_cl<To, From*const> {
|
||||
static inline bool doit(const From *Val) {
|
||||
assert(Val && "isa<> used on a null pointer");
|
||||
return isa_impl<To, From>::doit(*Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename To, typename From> struct isa_impl_cl<To, const From*> {
|
||||
static inline bool doit(const From *Val) {
|
||||
assert(Val && "isa<> used on a null pointer");
|
||||
return isa_impl<To, From>::doit(*Val);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename To, typename From> struct isa_impl_cl<To, const From*const> {
|
||||
static inline bool doit(const From *Val) {
|
||||
assert(Val && "isa<> used on a null pointer");
|
||||
return isa_impl<To, From>::doit(*Val);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename To, typename From, typename SimpleFrom>
|
||||
struct isa_impl_wrap {
|
||||
// When From != SimplifiedType, we can simplify the type some more by using
|
||||
// the simplify_type template.
|
||||
static bool doit(const From &Val) {
|
||||
return isa_impl_wrap<To, SimpleFrom,
|
||||
typename simplify_type<SimpleFrom>::SimpleType>::doit(
|
||||
simplify_type<const From>::getSimplifiedValue(Val));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename To, typename FromTy>
|
||||
struct isa_impl_wrap<To, FromTy, FromTy> {
|
||||
// When From == SimpleType, we are as simple as we are going to get.
|
||||
static bool doit(const FromTy &Val) {
|
||||
return isa_impl_cl<To,FromTy>::doit(Val);
|
||||
}
|
||||
};
|
||||
|
||||
// isa<X> - Return true if the parameter to the template is an instance of the
|
||||
// template type argument. Used like this:
|
||||
//
|
||||
// if (isa<Type>(myVal)) { ... }
|
||||
//
|
||||
template <class X, class Y> LLVM_NODISCARD inline bool isa(const Y &Val) {
|
||||
return isa_impl_wrap<X, const Y,
|
||||
typename simplify_type<const Y>::SimpleType>::doit(Val);
|
||||
}
|
||||
|
||||
// isa_and_nonnull<X> - Functionally identical to isa, except that a null value
|
||||
// is accepted.
|
||||
//
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline bool isa_and_nonnull(const Y &Val) {
|
||||
if (!Val)
|
||||
return false;
|
||||
return isa<X>(Val);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// cast<x> Support Templates
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
template<class To, class From> struct cast_retty;
|
||||
|
||||
// Calculate what type the 'cast' function should return, based on a requested
|
||||
// type of To and a source type of From.
|
||||
template<class To, class From> struct cast_retty_impl {
|
||||
using ret_type = To &; // Normal case, return Ty&
|
||||
};
|
||||
template<class To, class From> struct cast_retty_impl<To, const From> {
|
||||
using ret_type = const To &; // Normal case, return Ty&
|
||||
};
|
||||
|
||||
template<class To, class From> struct cast_retty_impl<To, From*> {
|
||||
using ret_type = To *; // Pointer arg case, return Ty*
|
||||
};
|
||||
|
||||
template<class To, class From> struct cast_retty_impl<To, const From*> {
|
||||
using ret_type = const To *; // Constant pointer arg case, return const Ty*
|
||||
};
|
||||
|
||||
template<class To, class From> struct cast_retty_impl<To, const From*const> {
|
||||
using ret_type = const To *; // Constant pointer arg case, return const Ty*
|
||||
};
|
||||
|
||||
template <class To, class From>
|
||||
struct cast_retty_impl<To, std::unique_ptr<From>> {
|
||||
private:
|
||||
using PointerType = typename cast_retty_impl<To, From *>::ret_type;
|
||||
using ResultType = typename std::remove_pointer<PointerType>::type;
|
||||
|
||||
public:
|
||||
using ret_type = std::unique_ptr<ResultType>;
|
||||
};
|
||||
|
||||
template<class To, class From, class SimpleFrom>
|
||||
struct cast_retty_wrap {
|
||||
// When the simplified type and the from type are not the same, use the type
|
||||
// simplifier to reduce the type, then reuse cast_retty_impl to get the
|
||||
// resultant type.
|
||||
using ret_type = typename cast_retty<To, SimpleFrom>::ret_type;
|
||||
};
|
||||
|
||||
template<class To, class FromTy>
|
||||
struct cast_retty_wrap<To, FromTy, FromTy> {
|
||||
// When the simplified type is equal to the from type, use it directly.
|
||||
using ret_type = typename cast_retty_impl<To,FromTy>::ret_type;
|
||||
};
|
||||
|
||||
template<class To, class From>
|
||||
struct cast_retty {
|
||||
using ret_type = typename cast_retty_wrap<
|
||||
To, From, typename simplify_type<From>::SimpleType>::ret_type;
|
||||
};
|
||||
|
||||
// Ensure the non-simple values are converted using the simplify_type template
|
||||
// that may be specialized by smart pointers...
|
||||
//
|
||||
template<class To, class From, class SimpleFrom> struct cast_convert_val {
|
||||
// This is not a simple type, use the template to simplify it...
|
||||
static typename cast_retty<To, From>::ret_type doit(From &Val) {
|
||||
return cast_convert_val<To, SimpleFrom,
|
||||
typename simplify_type<SimpleFrom>::SimpleType>::doit(
|
||||
simplify_type<From>::getSimplifiedValue(Val));
|
||||
}
|
||||
};
|
||||
|
||||
template<class To, class FromTy> struct cast_convert_val<To,FromTy,FromTy> {
|
||||
// This _is_ a simple type, just cast it.
|
||||
static typename cast_retty<To, FromTy>::ret_type doit(const FromTy &Val) {
|
||||
typename cast_retty<To, FromTy>::ret_type Res2
|
||||
= (typename cast_retty<To, FromTy>::ret_type)const_cast<FromTy&>(Val);
|
||||
return Res2;
|
||||
}
|
||||
};
|
||||
|
||||
template <class X> struct is_simple_type {
|
||||
static const bool value =
|
||||
std::is_same<X, typename simplify_type<X>::SimpleType>::value;
|
||||
};
|
||||
|
||||
// cast<X> - Return the argument parameter cast to the specified type. This
|
||||
// casting operator asserts that the type is correct, so it does not return null
|
||||
// on failure. It does not allow a null argument (use cast_or_null for that).
|
||||
// It is typically used like this:
|
||||
//
|
||||
// cast<Instruction>(myVal)->getParent()
|
||||
//
|
||||
template <class X, class Y>
|
||||
inline typename std::enable_if<!is_simple_type<Y>::value,
|
||||
typename cast_retty<X, const Y>::ret_type>::type
|
||||
cast(const Y &Val) {
|
||||
assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
|
||||
return cast_convert_val<
|
||||
X, const Y, typename simplify_type<const Y>::SimpleType>::doit(Val);
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
inline typename cast_retty<X, Y>::ret_type cast(Y &Val) {
|
||||
assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
|
||||
return cast_convert_val<X, Y,
|
||||
typename simplify_type<Y>::SimpleType>::doit(Val);
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
inline typename cast_retty<X, Y *>::ret_type cast(Y *Val) {
|
||||
assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
|
||||
return cast_convert_val<X, Y*,
|
||||
typename simplify_type<Y*>::SimpleType>::doit(Val);
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
inline typename cast_retty<X, std::unique_ptr<Y>>::ret_type
|
||||
cast(std::unique_ptr<Y> &&Val) {
|
||||
assert(isa<X>(Val.get()) && "cast<Ty>() argument of incompatible type!");
|
||||
using ret_type = typename cast_retty<X, std::unique_ptr<Y>>::ret_type;
|
||||
return ret_type(
|
||||
cast_convert_val<X, Y *, typename simplify_type<Y *>::SimpleType>::doit(
|
||||
Val.release()));
|
||||
}
|
||||
|
||||
// cast_or_null<X> - Functionally identical to cast, except that a null value is
|
||||
// accepted.
|
||||
//
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline
|
||||
typename std::enable_if<!is_simple_type<Y>::value,
|
||||
typename cast_retty<X, const Y>::ret_type>::type
|
||||
cast_or_null(const Y &Val) {
|
||||
if (!Val)
|
||||
return nullptr;
|
||||
assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
|
||||
return cast<X>(Val);
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline
|
||||
typename std::enable_if<!is_simple_type<Y>::value,
|
||||
typename cast_retty<X, Y>::ret_type>::type
|
||||
cast_or_null(Y &Val) {
|
||||
if (!Val)
|
||||
return nullptr;
|
||||
assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
|
||||
return cast<X>(Val);
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline typename cast_retty<X, Y *>::ret_type
|
||||
cast_or_null(Y *Val) {
|
||||
if (!Val) return nullptr;
|
||||
assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
|
||||
return cast<X>(Val);
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
inline typename cast_retty<X, std::unique_ptr<Y>>::ret_type
|
||||
cast_or_null(std::unique_ptr<Y> &&Val) {
|
||||
if (!Val)
|
||||
return nullptr;
|
||||
return cast<X>(std::move(Val));
|
||||
}
|
||||
|
||||
// dyn_cast<X> - Return the argument parameter cast to the specified type. This
|
||||
// casting operator returns null if the argument is of the wrong type, so it can
|
||||
// be used to test for a type as well as cast if successful. This should be
|
||||
// used in the context of an if statement like this:
|
||||
//
|
||||
// if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... }
|
||||
//
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline
|
||||
typename std::enable_if<!is_simple_type<Y>::value,
|
||||
typename cast_retty<X, const Y>::ret_type>::type
|
||||
dyn_cast(const Y &Val) {
|
||||
return isa<X>(Val) ? cast<X>(Val) : nullptr;
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline typename cast_retty<X, Y>::ret_type dyn_cast(Y &Val) {
|
||||
return isa<X>(Val) ? cast<X>(Val) : nullptr;
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline typename cast_retty<X, Y *>::ret_type dyn_cast(Y *Val) {
|
||||
return isa<X>(Val) ? cast<X>(Val) : nullptr;
|
||||
}
|
||||
|
||||
// dyn_cast_or_null<X> - Functionally identical to dyn_cast, except that a null
|
||||
// value is accepted.
|
||||
//
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline
|
||||
typename std::enable_if<!is_simple_type<Y>::value,
|
||||
typename cast_retty<X, const Y>::ret_type>::type
|
||||
dyn_cast_or_null(const Y &Val) {
|
||||
return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline
|
||||
typename std::enable_if<!is_simple_type<Y>::value,
|
||||
typename cast_retty<X, Y>::ret_type>::type
|
||||
dyn_cast_or_null(Y &Val) {
|
||||
return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline typename cast_retty<X, Y *>::ret_type
|
||||
dyn_cast_or_null(Y *Val) {
|
||||
return (Val && isa<X>(Val)) ? cast<X>(Val) : nullptr;
|
||||
}
|
||||
|
||||
// unique_dyn_cast<X> - Given a unique_ptr<Y>, try to return a unique_ptr<X>,
|
||||
// taking ownership of the input pointer iff isa<X>(Val) is true. If the
|
||||
// cast is successful, From refers to nullptr on exit and the casted value
|
||||
// is returned. If the cast is unsuccessful, the function returns nullptr
|
||||
// and From is unchanged.
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline auto unique_dyn_cast(std::unique_ptr<Y> &Val)
|
||||
-> decltype(cast<X>(Val)) {
|
||||
if (!isa<X>(Val))
|
||||
return nullptr;
|
||||
return cast<X>(std::move(Val));
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline auto unique_dyn_cast(std::unique_ptr<Y> &&Val)
|
||||
-> decltype(cast<X>(Val)) {
|
||||
return unique_dyn_cast<X, Y>(Val);
|
||||
}
|
||||
|
||||
// dyn_cast_or_null<X> - Functionally identical to unique_dyn_cast, except that
|
||||
// a null value is accepted.
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &Val)
|
||||
-> decltype(cast<X>(Val)) {
|
||||
if (!Val)
|
||||
return nullptr;
|
||||
return unique_dyn_cast<X, Y>(Val);
|
||||
}
|
||||
|
||||
template <class X, class Y>
|
||||
LLVM_NODISCARD inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &&Val)
|
||||
-> decltype(cast<X>(Val)) {
|
||||
return unique_dyn_cast_or_null<X, Y>(Val);
|
||||
}
|
||||
|
||||
} // end namespace llvm
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_LLVM_SUPPORT_CASTING_H
|
||||
@@ -1,590 +0,0 @@
|
||||
//===-- llvm/Support/Compiler.h - Compiler abstraction support --*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines several macros, based on the current compiler. This allows
|
||||
// use of compiler-specific features in a way that remains portable. This header
|
||||
// can be included from either C or C++.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
// - Some macros have been removed.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_SUPPORT_COMPILER_H
|
||||
#define OPENCOMBINE_LLVM_SUPPORT_COMPILER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <new>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <sal.h>
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
# define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_extension
|
||||
# define __has_extension(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
# define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in
|
||||
// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid.
|
||||
#ifndef LLVM_HAS_CPP_ATTRIBUTE
|
||||
#if defined(__cplusplus) && defined(__has_cpp_attribute)
|
||||
# define LLVM_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
|
||||
#else
|
||||
# define LLVM_HAS_CPP_ATTRIBUTE(x) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_GNUC_PREREQ
|
||||
/// Extend the default __GNUC_PREREQ even if glibc's features.h isn't
|
||||
/// available.
|
||||
#ifndef LLVM_GNUC_PREREQ
|
||||
# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
|
||||
# define LLVM_GNUC_PREREQ(maj, min, patch) \
|
||||
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \
|
||||
((maj) << 20) + ((min) << 10) + (patch))
|
||||
# elif defined(__GNUC__) && defined(__GNUC_MINOR__)
|
||||
# define LLVM_GNUC_PREREQ(maj, min, patch) \
|
||||
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
|
||||
# else
|
||||
# define LLVM_GNUC_PREREQ(maj, min, patch) 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_MSC_PREREQ
|
||||
/// Is the compiler MSVC of at least the specified version?
|
||||
/// The common \param version values to check for are:
|
||||
/// * 1910: VS2017, version 15.1 & 15.2
|
||||
/// * 1911: VS2017, version 15.3 & 15.4
|
||||
/// * 1912: VS2017, version 15.5
|
||||
/// * 1913: VS2017, version 15.6
|
||||
/// * 1914: VS2017, version 15.7
|
||||
/// * 1915: VS2017, version 15.8
|
||||
/// * 1916: VS2017, version 15.9
|
||||
/// * 1920: VS2019, version 16.0
|
||||
/// * 1921: VS2019, version 16.1
|
||||
#ifdef _MSC_VER
|
||||
#define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version))
|
||||
|
||||
// We require at least MSVC 2017.
|
||||
#if !LLVM_MSC_PREREQ(1910)
|
||||
#error LLVM requires at least MSVC 2017.
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define LLVM_MSC_PREREQ(version) 0
|
||||
#endif
|
||||
|
||||
/// Does the compiler support ref-qualifiers for *this?
|
||||
///
|
||||
/// Sadly, this is separate from just rvalue reference support because GCC
|
||||
/// and MSVC implemented this later than everything else.
|
||||
#if __has_feature(cxx_rvalue_references) || LLVM_GNUC_PREREQ(4, 8, 1)
|
||||
#define LLVM_HAS_RVALUE_REFERENCE_THIS 1
|
||||
#else
|
||||
#define LLVM_HAS_RVALUE_REFERENCE_THIS 0
|
||||
#endif
|
||||
|
||||
/// Expands to '&' if ref-qualifiers for *this are supported.
|
||||
///
|
||||
/// This can be used to provide lvalue/rvalue overrides of member functions.
|
||||
/// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS
|
||||
#if LLVM_HAS_RVALUE_REFERENCE_THIS
|
||||
#define LLVM_LVALUE_FUNCTION &
|
||||
#else
|
||||
#define LLVM_LVALUE_FUNCTION
|
||||
#endif
|
||||
|
||||
/// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked
|
||||
/// into a shared library, then the class should be private to the library and
|
||||
/// not accessible from outside it. Can also be used to mark variables and
|
||||
/// functions, making them private to any shared library they are linked into.
|
||||
/// On PE/COFF targets, library visibility is the default, so this isn't needed.
|
||||
#if (__has_attribute(visibility) || LLVM_GNUC_PREREQ(4, 0, 0)) && \
|
||||
!defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32)
|
||||
#define LLVM_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define LLVM_LIBRARY_VISIBILITY
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality)
|
||||
#else
|
||||
#define LLVM_PREFETCH(addr, rw, locality)
|
||||
#endif
|
||||
|
||||
#if __has_attribute(used) || LLVM_GNUC_PREREQ(3, 1, 0)
|
||||
#define LLVM_ATTRIBUTE_USED __attribute__((__used__))
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_USED
|
||||
#endif
|
||||
|
||||
/// LLVM_NODISCARD - Warn if a type or return value is discarded.
|
||||
|
||||
// Use the 'nodiscard' attribute in C++17 or newer mode.
|
||||
#if __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(nodiscard)
|
||||
#define LLVM_NODISCARD [[nodiscard]]
|
||||
#elif LLVM_HAS_CPP_ATTRIBUTE(clang::warn_unused_result)
|
||||
#define LLVM_NODISCARD [[clang::warn_unused_result]]
|
||||
// Clang in C++14 mode claims that it has the 'nodiscard' attribute, but also
|
||||
// warns in the pedantic mode that 'nodiscard' is a C++17 extension (PR33518).
|
||||
// Use the 'nodiscard' attribute in C++14 mode only with GCC.
|
||||
// TODO: remove this workaround when PR33518 is resolved.
|
||||
#elif defined(__GNUC__) && LLVM_HAS_CPP_ATTRIBUTE(nodiscard)
|
||||
#define LLVM_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
#define LLVM_NODISCARD
|
||||
#endif
|
||||
|
||||
// Indicate that a non-static, non-const C++ member function reinitializes
|
||||
// the entire object to a known state, independent of the previous state of
|
||||
// the object.
|
||||
//
|
||||
// The clang-tidy check bugprone-use-after-move recognizes this attribute as a
|
||||
// marker that a moved-from object has left the indeterminate state and can be
|
||||
// reused.
|
||||
#if LLVM_HAS_CPP_ATTRIBUTE(clang::reinitializes)
|
||||
#define LLVM_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]]
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_REINITIALIZES
|
||||
#endif
|
||||
|
||||
// Some compilers warn about unused functions. When a function is sometimes
|
||||
// used or not depending on build settings (e.g. a function only called from
|
||||
// within "assert"), this attribute can be used to suppress such warnings.
|
||||
//
|
||||
// However, it shouldn't be used for unused *variables*, as those have a much
|
||||
// more portable solution:
|
||||
// (void)unused_var_name;
|
||||
// Prefer cast-to-void wherever it is sufficient.
|
||||
#if __has_attribute(unused) || LLVM_GNUC_PREREQ(3, 1, 0)
|
||||
#define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__))
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_UNUSED
|
||||
#endif
|
||||
|
||||
// FIXME: Provide this for PE/COFF targets.
|
||||
#if (__has_attribute(weak) || LLVM_GNUC_PREREQ(4, 0, 0)) && \
|
||||
(!defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32))
|
||||
#define LLVM_ATTRIBUTE_WEAK __attribute__((__weak__))
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_WEAK
|
||||
#endif
|
||||
|
||||
// Prior to clang 3.2, clang did not accept any spelling of
|
||||
// __has_attribute(const), so assume it is supported.
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
// aka 'CONST' but following LLVM Conventions.
|
||||
#define LLVM_READNONE __attribute__((__const__))
|
||||
#else
|
||||
#define LLVM_READNONE
|
||||
#endif
|
||||
|
||||
#if __has_attribute(pure) || defined(__GNUC__)
|
||||
// aka 'PURE' but following LLVM Conventions.
|
||||
#define LLVM_READONLY __attribute__((__pure__))
|
||||
#else
|
||||
#define LLVM_READONLY
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_expect) || LLVM_GNUC_PREREQ(4, 0, 0)
|
||||
#define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
|
||||
#define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
|
||||
#else
|
||||
#define LLVM_LIKELY(EXPR) (EXPR)
|
||||
#define LLVM_UNLIKELY(EXPR) (EXPR)
|
||||
#endif
|
||||
|
||||
/// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so,
|
||||
/// mark a method "not for inlining".
|
||||
#if __has_attribute(noinline) || LLVM_GNUC_PREREQ(3, 4, 0)
|
||||
#define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define LLVM_ATTRIBUTE_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_NOINLINE
|
||||
#endif
|
||||
|
||||
/// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do
|
||||
/// so, mark a method "always inline" because it is performance sensitive. GCC
|
||||
/// 3.4 supported this but is buggy in various cases and produces unimplemented
|
||||
/// errors, just use it in GCC 4.0 and later.
|
||||
#if __has_attribute(always_inline) || LLVM_GNUC_PREREQ(4, 0, 0)
|
||||
#define LLVM_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define LLVM_ATTRIBUTE_ALWAYS_INLINE __forceinline
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LLVM_ATTRIBUTE_NORETURN __attribute__((noreturn))
|
||||
#elif defined(_MSC_VER)
|
||||
#define LLVM_ATTRIBUTE_NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_NORETURN
|
||||
#endif
|
||||
|
||||
#if __has_attribute(returns_nonnull) || LLVM_GNUC_PREREQ(4, 9, 0)
|
||||
#define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
|
||||
#elif defined(_MSC_VER)
|
||||
#define LLVM_ATTRIBUTE_RETURNS_NONNULL _Ret_notnull_
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_RETURNS_NONNULL
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a
|
||||
/// pointer that does not alias any other valid pointer.
|
||||
#ifdef __GNUC__
|
||||
#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
|
||||
#elif defined(_MSC_VER)
|
||||
#define LLVM_ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict)
|
||||
#else
|
||||
#define LLVM_ATTRIBUTE_RETURNS_NOALIAS
|
||||
#endif
|
||||
|
||||
/// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
|
||||
#if __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(fallthrough)
|
||||
#define LLVM_FALLTHROUGH [[fallthrough]]
|
||||
#elif LLVM_HAS_CPP_ATTRIBUTE(gnu::fallthrough)
|
||||
#define LLVM_FALLTHROUGH [[gnu::fallthrough]]
|
||||
#elif __has_attribute(fallthrough)
|
||||
#define LLVM_FALLTHROUGH __attribute__((fallthrough))
|
||||
#elif LLVM_HAS_CPP_ATTRIBUTE(clang::fallthrough)
|
||||
#define LLVM_FALLTHROUGH [[clang::fallthrough]]
|
||||
#else
|
||||
#define LLVM_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
/// LLVM_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that
|
||||
/// they are constant initialized.
|
||||
#if LLVM_HAS_CPP_ATTRIBUTE(clang::require_constant_initialization)
|
||||
#define LLVM_REQUIRE_CONSTANT_INITIALIZATION \
|
||||
[[clang::require_constant_initialization]]
|
||||
#else
|
||||
#define LLVM_REQUIRE_CONSTANT_INITIALIZATION
|
||||
#endif
|
||||
|
||||
/// LLVM_EXTENSION - Support compilers where we have a keyword to suppress
|
||||
/// pedantic diagnostics.
|
||||
#ifdef __GNUC__
|
||||
#define LLVM_EXTENSION __extension__
|
||||
#else
|
||||
#define LLVM_EXTENSION
|
||||
#endif
|
||||
|
||||
// LLVM_ATTRIBUTE_DEPRECATED(decl, "message")
|
||||
#if __has_feature(attribute_deprecated_with_message)
|
||||
# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
|
||||
decl __attribute__((deprecated(message)))
|
||||
#elif defined(__GNUC__)
|
||||
# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
|
||||
decl __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
|
||||
__declspec(deprecated(message)) decl
|
||||
#else
|
||||
# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \
|
||||
decl
|
||||
#endif
|
||||
|
||||
/// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands
|
||||
/// to an expression which states that it is undefined behavior for the
|
||||
/// compiler to reach this point. Otherwise is not defined.
|
||||
#if __has_builtin(__builtin_unreachable) || LLVM_GNUC_PREREQ(4, 5, 0)
|
||||
# define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
|
||||
#elif defined(_MSC_VER)
|
||||
# define LLVM_BUILTIN_UNREACHABLE __assume(false)
|
||||
#endif
|
||||
|
||||
/// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression
|
||||
/// which causes the program to exit abnormally.
|
||||
#if __has_builtin(__builtin_trap) || LLVM_GNUC_PREREQ(4, 3, 0)
|
||||
# define LLVM_BUILTIN_TRAP __builtin_trap()
|
||||
#elif defined(_MSC_VER)
|
||||
// The __debugbreak intrinsic is supported by MSVC, does not require forward
|
||||
// declarations involving platform-specific typedefs (unlike RaiseException),
|
||||
// results in a call to vectored exception handlers, and encodes to a short
|
||||
// instruction that still causes the trapping behavior we want.
|
||||
# define LLVM_BUILTIN_TRAP __debugbreak()
|
||||
#else
|
||||
# define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0
|
||||
#endif
|
||||
|
||||
/// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to
|
||||
/// an expression which causes the program to break while running
|
||||
/// under a debugger.
|
||||
#if __has_builtin(__builtin_debugtrap)
|
||||
# define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap()
|
||||
#elif defined(_MSC_VER)
|
||||
// The __debugbreak intrinsic is supported by MSVC and breaks while
|
||||
// running under the debugger, and also supports invoking a debugger
|
||||
// when the OS is configured appropriately.
|
||||
# define LLVM_BUILTIN_DEBUGTRAP __debugbreak()
|
||||
#else
|
||||
// Just continue execution when built with compilers that have no
|
||||
// support. This is a debugging aid and not intended to force the
|
||||
// program to abort if encountered.
|
||||
# define LLVM_BUILTIN_DEBUGTRAP
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_ASSUME_ALIGNED
|
||||
/// Returns a pointer with an assumed alignment.
|
||||
#if __has_builtin(__builtin_assume_aligned) || LLVM_GNUC_PREREQ(4, 7, 0)
|
||||
# define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a)
|
||||
#elif defined(LLVM_BUILTIN_UNREACHABLE)
|
||||
// As of today, clang does not support __builtin_assume_aligned.
|
||||
# define LLVM_ASSUME_ALIGNED(p, a) \
|
||||
(((uintptr_t(p) % (a)) == 0) ? (p) : (LLVM_BUILTIN_UNREACHABLE, (p)))
|
||||
#else
|
||||
# define LLVM_ASSUME_ALIGNED(p, a) (p)
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_PACKED
|
||||
/// Used to specify a packed structure.
|
||||
/// LLVM_PACKED(
|
||||
/// struct A {
|
||||
/// int i;
|
||||
/// int j;
|
||||
/// int k;
|
||||
/// long long l;
|
||||
/// });
|
||||
///
|
||||
/// LLVM_PACKED_START
|
||||
/// struct B {
|
||||
/// int i;
|
||||
/// int j;
|
||||
/// int k;
|
||||
/// long long l;
|
||||
/// };
|
||||
/// LLVM_PACKED_END
|
||||
#ifdef _MSC_VER
|
||||
# define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop))
|
||||
# define LLVM_PACKED_START __pragma(pack(push, 1))
|
||||
# define LLVM_PACKED_END __pragma(pack(pop))
|
||||
#else
|
||||
# define LLVM_PACKED(d) d __attribute__((packed))
|
||||
# define LLVM_PACKED_START _Pragma("pack(push, 1)")
|
||||
# define LLVM_PACKED_END _Pragma("pack(pop)")
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_PTR_SIZE
|
||||
/// A constant integer equivalent to the value of sizeof(void*).
|
||||
/// Generally used in combination with alignas or when doing computation in the
|
||||
/// preprocessor.
|
||||
#ifdef __SIZEOF_POINTER__
|
||||
# define LLVM_PTR_SIZE __SIZEOF_POINTER__
|
||||
#elif defined(_WIN64)
|
||||
# define LLVM_PTR_SIZE 8
|
||||
#elif defined(_WIN32)
|
||||
# define LLVM_PTR_SIZE 4
|
||||
#elif defined(_MSC_VER)
|
||||
# error "could not determine LLVM_PTR_SIZE as a constant int for MSVC"
|
||||
#else
|
||||
# define LLVM_PTR_SIZE sizeof(void *)
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_MEMORY_SANITIZER_BUILD
|
||||
/// Whether LLVM itself is built with MemorySanitizer instrumentation.
|
||||
#if __has_feature(memory_sanitizer)
|
||||
# define LLVM_MEMORY_SANITIZER_BUILD 1
|
||||
# include <sanitizer/msan_interface.h>
|
||||
#else
|
||||
# define LLVM_MEMORY_SANITIZER_BUILD 0
|
||||
# define __msan_allocated_memory(p, size)
|
||||
# define __msan_unpoison(p, size)
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_ADDRESS_SANITIZER_BUILD
|
||||
/// Whether LLVM itself is built with AddressSanitizer instrumentation.
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
# define LLVM_ADDRESS_SANITIZER_BUILD 1
|
||||
# include <sanitizer/asan_interface.h>
|
||||
#else
|
||||
# define LLVM_ADDRESS_SANITIZER_BUILD 0
|
||||
# define __asan_poison_memory_region(p, size)
|
||||
# define __asan_unpoison_memory_region(p, size)
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_THREAD_SANITIZER_BUILD
|
||||
/// Whether LLVM itself is built with ThreadSanitizer instrumentation.
|
||||
#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
|
||||
# define LLVM_THREAD_SANITIZER_BUILD 1
|
||||
#else
|
||||
# define LLVM_THREAD_SANITIZER_BUILD 0
|
||||
#endif
|
||||
|
||||
#if LLVM_THREAD_SANITIZER_BUILD
|
||||
// Thread Sanitizer is a tool that finds races in code.
|
||||
// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations .
|
||||
// tsan detects these exact functions by name.
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void AnnotateHappensAfter(const char *file, int line, const volatile void *cv);
|
||||
void AnnotateHappensBefore(const char *file, int line, const volatile void *cv);
|
||||
void AnnotateIgnoreWritesBegin(const char *file, int line);
|
||||
void AnnotateIgnoreWritesEnd(const char *file, int line);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// This marker is used to define a happens-before arc. The race detector will
|
||||
// infer an arc from the begin to the end when they share the same pointer
|
||||
// argument.
|
||||
# define TsanHappensBefore(cv) AnnotateHappensBefore(__FILE__, __LINE__, cv)
|
||||
|
||||
// This marker defines the destination of a happens-before arc.
|
||||
# define TsanHappensAfter(cv) AnnotateHappensAfter(__FILE__, __LINE__, cv)
|
||||
|
||||
// Ignore any races on writes between here and the next TsanIgnoreWritesEnd.
|
||||
# define TsanIgnoreWritesBegin() AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
|
||||
|
||||
// Resume checking for racy writes.
|
||||
# define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
|
||||
#else
|
||||
# define TsanHappensBefore(cv)
|
||||
# define TsanHappensAfter(cv)
|
||||
# define TsanIgnoreWritesBegin()
|
||||
# define TsanIgnoreWritesEnd()
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_NO_SANITIZE
|
||||
/// Disable a particular sanitizer for a function.
|
||||
#if __has_attribute(no_sanitize)
|
||||
#define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND)))
|
||||
#else
|
||||
#define LLVM_NO_SANITIZE(KIND)
|
||||
#endif
|
||||
|
||||
/// Mark debug helper function definitions like dump() that should not be
|
||||
/// stripped from debug builds.
|
||||
/// Note that you should also surround dump() functions with
|
||||
/// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always
|
||||
/// get stripped in release builds.
|
||||
// FIXME: Move this to a private config.h as it's not usable in public headers.
|
||||
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
||||
#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
|
||||
#else
|
||||
#define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_PRETTY_FUNCTION
|
||||
/// Gets a user-friendly looking function signature for the current scope
|
||||
/// using the best available method on each platform. The exact format of the
|
||||
/// resulting string is implementation specific and non-portable, so this should
|
||||
/// only be used, for example, for logging or diagnostics.
|
||||
#if defined(_MSC_VER)
|
||||
#define LLVM_PRETTY_FUNCTION __FUNCSIG__
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define LLVM_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
#else
|
||||
#define LLVM_PRETTY_FUNCTION __func__
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_THREAD_LOCAL
|
||||
/// A thread-local storage specifier which can be used with globals,
|
||||
/// extern globals, and static globals.
|
||||
///
|
||||
/// This is essentially an extremely restricted analog to C++11's thread_local
|
||||
/// support, and uses that when available. However, it falls back on
|
||||
/// platform-specific or vendor-provided extensions when necessary. These
|
||||
/// extensions don't support many of the C++11 thread_local's features. You
|
||||
/// should only use this for PODs that you can statically initialize to
|
||||
/// some constant value. In almost all circumstances this is most appropriate
|
||||
/// for use with a pointer, integer, or small aggregation of pointers and
|
||||
/// integers.
|
||||
#if LLVM_ENABLE_THREADS
|
||||
#if __has_feature(cxx_thread_local)
|
||||
#define LLVM_THREAD_LOCAL thread_local
|
||||
#elif defined(_MSC_VER)
|
||||
// MSVC supports this with a __declspec.
|
||||
#define LLVM_THREAD_LOCAL __declspec(thread)
|
||||
#else
|
||||
// Clang, GCC, and other compatible compilers used __thread prior to C++11 and
|
||||
// we only need the restricted functionality that provides.
|
||||
#define LLVM_THREAD_LOCAL __thread
|
||||
#endif
|
||||
#else // !LLVM_ENABLE_THREADS
|
||||
// If threading is disabled entirely, this compiles to nothing and you get
|
||||
// a normal global variable.
|
||||
#define LLVM_THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_ENABLE_EXCEPTIONS
|
||||
/// Whether LLVM is built with exception support.
|
||||
#if __has_feature(cxx_exceptions)
|
||||
#define LLVM_ENABLE_EXCEPTIONS 1
|
||||
#elif defined(__GNUC__) && defined(__EXCEPTIONS)
|
||||
#define LLVM_ENABLE_EXCEPTIONS 1
|
||||
#elif defined(_MSC_VER) && defined(_CPPUNWIND)
|
||||
#define LLVM_ENABLE_EXCEPTIONS 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
|
||||
/// Allocate a buffer of memory with the given size and alignment.
|
||||
///
|
||||
/// When the compiler supports aligned operator new, this will use it to to
|
||||
/// handle even over-aligned allocations.
|
||||
///
|
||||
/// However, this doesn't make any attempt to leverage the fancier techniques
|
||||
/// like posix_memalign due to portability. It is mostly intended to allow
|
||||
/// compatibility with platforms that, after aligned allocation was added, use
|
||||
/// reduced default alignment.
|
||||
inline void *allocate_buffer(size_t Size, size_t Alignment) {
|
||||
return ::operator new(Size
|
||||
#ifdef __cpp_aligned_new
|
||||
,
|
||||
std::align_val_t(Alignment)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
/// Deallocate a buffer of memory with the given size and alignment.
|
||||
///
|
||||
/// If supported, this will used the sized delete operator. Also if supported,
|
||||
/// this will pass the alignment to the delete operator.
|
||||
///
|
||||
/// The pointer must have been allocated with the corresponding new operator,
|
||||
/// most likely using the above helper.
|
||||
inline void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) {
|
||||
::operator delete(Ptr
|
||||
#ifdef __cpp_sized_deallocation
|
||||
,
|
||||
Size
|
||||
#endif
|
||||
#ifdef __cpp_aligned_new
|
||||
,
|
||||
std::align_val_t(Alignment)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
} // End namespace llvm
|
||||
} // End namespace opencombine
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif
|
||||
@@ -1,94 +0,0 @@
|
||||
/*===-- include/llvm-c/DataTypes.h - Define fixed size types ------*- C -*-===*\
|
||||
|* *|
|
||||
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
|
||||
|* Exceptions. *|
|
||||
|* See https://llvm.org/LICENSE.txt for license information. *|
|
||||
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
|
||||
|* *|
|
||||
|*===----------------------------------------------------------------------===*|
|
||||
|* *|
|
||||
|* This file contains definitions to figure out the size of _HOST_ data types.*|
|
||||
|* This file is important because different host OS's define different macros,*|
|
||||
|* which makes portability tough. This file exports the following *|
|
||||
|* definitions: *|
|
||||
|* *|
|
||||
|* [u]int(32|64)_t : typedefs for signed and unsigned 32/64 bit system types*|
|
||||
|* [U]INT(8|16|32|64)_(MIN|MAX) : Constants for the min and max values. *|
|
||||
|* *|
|
||||
|* No library is required when using these functions. *|
|
||||
|* *|
|
||||
|*===----------------------------------------------------------------------===*/
|
||||
|
||||
/* Please leave this file C-compatible. */
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_C_DATATYPES_H
|
||||
#define OPENCOMBINE_LLVM_C_DATATYPES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cmath>
|
||||
#else
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#if !defined(UINT32_MAX)
|
||||
# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
|
||||
"__STDC_LIMIT_MACROS before #including llvm-c/DataTypes.h"
|
||||
#endif
|
||||
|
||||
#if !defined(UINT32_C)
|
||||
# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
|
||||
"__STDC_CONSTANT_MACROS before #including llvm-c/DataTypes.h"
|
||||
#endif
|
||||
|
||||
/* Note that <inttypes.h> includes <stdint.h>, if this is a C99 system. */
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _AIX
|
||||
// GCC is strict about defining large constants: they must have LL modifier.
|
||||
#undef INT64_MAX
|
||||
#undef INT64_MIN
|
||||
#endif
|
||||
|
||||
#else /* _MSC_VER */
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed __int64 ssize_t;
|
||||
#else
|
||||
typedef signed int ssize_t;
|
||||
#endif /* _WIN64 */
|
||||
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
/* Set defaults for constants which we cannot find. */
|
||||
#if !defined(INT64_MAX)
|
||||
# define INT64_MAX 9223372036854775807LL
|
||||
#endif
|
||||
#if !defined(INT64_MIN)
|
||||
# define INT64_MIN ((-INT64_MAX)-1)
|
||||
#endif
|
||||
#if !defined(UINT64_MAX)
|
||||
# define UINT64_MAX 0xffffffffffffffffULL
|
||||
#endif
|
||||
|
||||
#ifndef HUGE_VALF
|
||||
#define HUGE_VALF (float)HUGE_VAL
|
||||
#endif
|
||||
|
||||
#endif /* OPENCOMBINE_LLVM_C_DATATYPES_H */
|
||||
@@ -1,957 +0,0 @@
|
||||
//===-- llvm/Support/MathExtras.h - Useful math functions -------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains some functions that are useful for math stuff.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_SUPPORT_MATHEXTRAS_H
|
||||
#define OPENCOMBINE_LLVM_SUPPORT_MATHEXTRAS_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/SwapByteOrder.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef __ANDROID_NDK__
|
||||
#include <android/api-level.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Declare these intrinsics manually rather including intrin.h. It's very
|
||||
// expensive, and MathExtras.h is popular.
|
||||
// #include <intrin.h>
|
||||
extern "C" {
|
||||
unsigned char _BitScanForward(unsigned long *_Index, unsigned long _Mask);
|
||||
unsigned char _BitScanForward64(unsigned long *_Index, unsigned __int64 _Mask);
|
||||
unsigned char _BitScanReverse(unsigned long *_Index, unsigned long _Mask);
|
||||
unsigned char _BitScanReverse64(unsigned long *_Index, unsigned __int64 _Mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
|
||||
/// The behavior an operation has on an input of 0.
|
||||
enum ZeroBehavior {
|
||||
/// The returned value is undefined.
|
||||
ZB_Undefined,
|
||||
/// The returned value is numeric_limits<T>::max()
|
||||
ZB_Max,
|
||||
/// The returned value is numeric_limits<T>::digits
|
||||
ZB_Width
|
||||
};
|
||||
|
||||
/// Mathematical constants.
|
||||
namespace numbers {
|
||||
// TODO: Track C++20 std::numbers.
|
||||
// TODO: Favor using the hexadecimal FP constants (requires C++17).
|
||||
constexpr double e = 2.7182818284590452354, // (0x1.5bf0a8b145749P+1) https://oeis.org/A001113
|
||||
egamma = .57721566490153286061, // (0x1.2788cfc6fb619P-1) https://oeis.org/A001620
|
||||
ln2 = .69314718055994530942, // (0x1.62e42fefa39efP-1) https://oeis.org/A002162
|
||||
ln10 = 2.3025850929940456840, // (0x1.24bb1bbb55516P+1) https://oeis.org/A002392
|
||||
log2e = 1.4426950408889634074, // (0x1.71547652b82feP+0)
|
||||
log10e = .43429448190325182765, // (0x1.bcb7b1526e50eP-2)
|
||||
pi = 3.1415926535897932385, // (0x1.921fb54442d18P+1) https://oeis.org/A000796
|
||||
inv_pi = .31830988618379067154, // (0x1.45f306bc9c883P-2) https://oeis.org/A049541
|
||||
sqrtpi = 1.7724538509055160273, // (0x1.c5bf891b4ef6bP+0) https://oeis.org/A002161
|
||||
inv_sqrtpi = .56418958354775628695, // (0x1.20dd750429b6dP-1) https://oeis.org/A087197
|
||||
sqrt2 = 1.4142135623730950488, // (0x1.6a09e667f3bcdP+0) https://oeis.org/A00219
|
||||
inv_sqrt2 = .70710678118654752440, // (0x1.6a09e667f3bcdP-1)
|
||||
sqrt3 = 1.7320508075688772935, // (0x1.bb67ae8584caaP+0) https://oeis.org/A002194
|
||||
inv_sqrt3 = .57735026918962576451, // (0x1.279a74590331cP-1)
|
||||
phi = 1.6180339887498948482; // (0x1.9e3779b97f4a8P+0) https://oeis.org/A001622
|
||||
constexpr float ef = 2.71828183F, // (0x1.5bf0a8P+1) https://oeis.org/A001113
|
||||
egammaf = .577215665F, // (0x1.2788d0P-1) https://oeis.org/A001620
|
||||
ln2f = .693147181F, // (0x1.62e430P-1) https://oeis.org/A002162
|
||||
ln10f = 2.30258509F, // (0x1.26bb1cP+1) https://oeis.org/A002392
|
||||
log2ef = 1.44269504F, // (0x1.715476P+0)
|
||||
log10ef = .434294482F, // (0x1.bcb7b2P-2)
|
||||
pif = 3.14159265F, // (0x1.921fb6P+1) https://oeis.org/A000796
|
||||
inv_pif = .318309886F, // (0x1.45f306P-2) https://oeis.org/A049541
|
||||
sqrtpif = 1.77245385F, // (0x1.c5bf8aP+0) https://oeis.org/A002161
|
||||
inv_sqrtpif = .564189584F, // (0x1.20dd76P-1) https://oeis.org/A087197
|
||||
sqrt2f = 1.41421356F, // (0x1.6a09e6P+0) https://oeis.org/A002193
|
||||
inv_sqrt2f = .707106781F, // (0x1.6a09e6P-1)
|
||||
sqrt3f = 1.73205081F, // (0x1.bb67aeP+0) https://oeis.org/A002194
|
||||
inv_sqrt3f = .577350269F, // (0x1.279a74P-1)
|
||||
phif = 1.61803399F; // (0x1.9e377aP+0) https://oeis.org/A001622
|
||||
} // namespace numbers
|
||||
|
||||
namespace detail {
|
||||
template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
|
||||
static unsigned count(T Val, ZeroBehavior) {
|
||||
if (!Val)
|
||||
return std::numeric_limits<T>::digits;
|
||||
if (Val & 0x1)
|
||||
return 0;
|
||||
|
||||
// Bisection method.
|
||||
unsigned ZeroBits = 0;
|
||||
T Shift = std::numeric_limits<T>::digits >> 1;
|
||||
T Mask = std::numeric_limits<T>::max() >> Shift;
|
||||
while (Shift) {
|
||||
if ((Val & Mask) == 0) {
|
||||
Val >>= Shift;
|
||||
ZeroBits |= Shift;
|
||||
}
|
||||
Shift >>= 1;
|
||||
Mask >>= Shift;
|
||||
}
|
||||
return ZeroBits;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||
template <typename T> struct TrailingZerosCounter<T, 4> {
|
||||
static unsigned count(T Val, ZeroBehavior ZB) {
|
||||
if (ZB != ZB_Undefined && Val == 0)
|
||||
return 32;
|
||||
|
||||
#if __has_builtin(__builtin_ctz) || defined(__GNUC__)
|
||||
return __builtin_ctz(Val);
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned long Index;
|
||||
_BitScanForward(&Index, Val);
|
||||
return Index;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(_MSC_VER) || defined(_M_X64)
|
||||
template <typename T> struct TrailingZerosCounter<T, 8> {
|
||||
static unsigned count(T Val, ZeroBehavior ZB) {
|
||||
if (ZB != ZB_Undefined && Val == 0)
|
||||
return 64;
|
||||
|
||||
#if __has_builtin(__builtin_ctzll) || defined(__GNUC__)
|
||||
return __builtin_ctzll(Val);
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned long Index;
|
||||
_BitScanForward64(&Index, Val);
|
||||
return Index;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
/// Count number of 0's from the least significant bit to the most
|
||||
/// stopping at the first 1.
|
||||
///
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
|
||||
/// valid arguments.
|
||||
template <typename T>
|
||||
unsigned countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
|
||||
static_assert(std::numeric_limits<T>::is_integer &&
|
||||
!std::numeric_limits<T>::is_signed,
|
||||
"Only unsigned integral types are allowed.");
|
||||
return llvm::detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter {
|
||||
static unsigned count(T Val, ZeroBehavior) {
|
||||
if (!Val)
|
||||
return std::numeric_limits<T>::digits;
|
||||
|
||||
// Bisection method.
|
||||
unsigned ZeroBits = 0;
|
||||
for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
|
||||
T Tmp = Val >> Shift;
|
||||
if (Tmp)
|
||||
Val = Tmp;
|
||||
else
|
||||
ZeroBits |= Shift;
|
||||
}
|
||||
return ZeroBits;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||
template <typename T> struct LeadingZerosCounter<T, 4> {
|
||||
static unsigned count(T Val, ZeroBehavior ZB) {
|
||||
if (ZB != ZB_Undefined && Val == 0)
|
||||
return 32;
|
||||
|
||||
#if __has_builtin(__builtin_clz) || defined(__GNUC__)
|
||||
return __builtin_clz(Val);
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned long Index;
|
||||
_BitScanReverse(&Index, Val);
|
||||
return Index ^ 31;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(_MSC_VER) || defined(_M_X64)
|
||||
template <typename T> struct LeadingZerosCounter<T, 8> {
|
||||
static unsigned count(T Val, ZeroBehavior ZB) {
|
||||
if (ZB != ZB_Undefined && Val == 0)
|
||||
return 64;
|
||||
|
||||
#if __has_builtin(__builtin_clzll) || defined(__GNUC__)
|
||||
return __builtin_clzll(Val);
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned long Index;
|
||||
_BitScanReverse64(&Index, Val);
|
||||
return Index ^ 63;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
/// Count number of 0's from the most significant bit to the least
|
||||
/// stopping at the first 1.
|
||||
///
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
|
||||
/// valid arguments.
|
||||
template <typename T>
|
||||
unsigned countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
|
||||
static_assert(std::numeric_limits<T>::is_integer &&
|
||||
!std::numeric_limits<T>::is_signed,
|
||||
"Only unsigned integral types are allowed.");
|
||||
return llvm::detail::LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB);
|
||||
}
|
||||
|
||||
/// Get the index of the first set bit starting from the least
|
||||
/// significant bit.
|
||||
///
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
|
||||
/// valid arguments.
|
||||
template <typename T> T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
|
||||
if (ZB == ZB_Max && Val == 0)
|
||||
return std::numeric_limits<T>::max();
|
||||
|
||||
return countTrailingZeros(Val, ZB_Undefined);
|
||||
}
|
||||
|
||||
/// Create a bitmask with the N right-most bits set to 1, and all other
|
||||
/// bits set to 0. Only unsigned types are allowed.
|
||||
template <typename T> T maskTrailingOnes(unsigned N) {
|
||||
static_assert(std::is_unsigned<T>::value, "Invalid type!");
|
||||
const unsigned Bits = CHAR_BIT * sizeof(T);
|
||||
assert(N <= Bits && "Invalid bit index");
|
||||
return N == 0 ? 0 : (T(-1) >> (Bits - N));
|
||||
}
|
||||
|
||||
/// Create a bitmask with the N left-most bits set to 1, and all other
|
||||
/// bits set to 0. Only unsigned types are allowed.
|
||||
template <typename T> T maskLeadingOnes(unsigned N) {
|
||||
return ~maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
|
||||
}
|
||||
|
||||
/// Create a bitmask with the N right-most bits set to 0, and all other
|
||||
/// bits set to 1. Only unsigned types are allowed.
|
||||
template <typename T> T maskTrailingZeros(unsigned N) {
|
||||
return maskLeadingOnes<T>(CHAR_BIT * sizeof(T) - N);
|
||||
}
|
||||
|
||||
/// Create a bitmask with the N left-most bits set to 0, and all other
|
||||
/// bits set to 1. Only unsigned types are allowed.
|
||||
template <typename T> T maskLeadingZeros(unsigned N) {
|
||||
return maskTrailingOnes<T>(CHAR_BIT * sizeof(T) - N);
|
||||
}
|
||||
|
||||
/// Get the index of the last set bit starting from the least
|
||||
/// significant bit.
|
||||
///
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
|
||||
/// valid arguments.
|
||||
template <typename T> T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) {
|
||||
if (ZB == ZB_Max && Val == 0)
|
||||
return std::numeric_limits<T>::max();
|
||||
|
||||
// Use ^ instead of - because both gcc and llvm can remove the associated ^
|
||||
// in the __builtin_clz intrinsic on x86.
|
||||
return countLeadingZeros(Val, ZB_Undefined) ^
|
||||
(std::numeric_limits<T>::digits - 1);
|
||||
}
|
||||
|
||||
/// Macro compressed bit reversal table for 256 bits.
|
||||
///
|
||||
/// http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
|
||||
static const unsigned char BitReverseTable256[256] = {
|
||||
#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64
|
||||
#define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16)
|
||||
#define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4)
|
||||
R6(0), R6(2), R6(1), R6(3)
|
||||
#undef R2
|
||||
#undef R4
|
||||
#undef R6
|
||||
};
|
||||
|
||||
/// Reverse the bits in \p Val.
|
||||
template <typename T>
|
||||
T reverseBits(T Val) {
|
||||
unsigned char in[sizeof(Val)];
|
||||
unsigned char out[sizeof(Val)];
|
||||
std::memcpy(in, &Val, sizeof(Val));
|
||||
for (unsigned i = 0; i < sizeof(Val); ++i)
|
||||
out[(sizeof(Val) - i) - 1] = BitReverseTable256[in[i]];
|
||||
std::memcpy(&Val, out, sizeof(Val));
|
||||
return Val;
|
||||
}
|
||||
|
||||
// NOTE: The following support functions use the _32/_64 extensions instead of
|
||||
// type overloading so that signed and unsigned integers can be used without
|
||||
// ambiguity.
|
||||
|
||||
/// Return the high 32 bits of a 64 bit value.
|
||||
constexpr inline uint32_t Hi_32(uint64_t Value) {
|
||||
return static_cast<uint32_t>(Value >> 32);
|
||||
}
|
||||
|
||||
/// Return the low 32 bits of a 64 bit value.
|
||||
constexpr inline uint32_t Lo_32(uint64_t Value) {
|
||||
return static_cast<uint32_t>(Value);
|
||||
}
|
||||
|
||||
/// Make a 64-bit integer from a high / low pair of 32-bit integers.
|
||||
constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) {
|
||||
return ((uint64_t)High << 32) | (uint64_t)Low;
|
||||
}
|
||||
|
||||
/// Checks if an integer fits into the given bit width.
|
||||
template <unsigned N> constexpr inline bool isInt(int64_t x) {
|
||||
return N >= 64 || (-(INT64_C(1)<<(N-1)) <= x && x < (INT64_C(1)<<(N-1)));
|
||||
}
|
||||
// Template specializations to get better code for common cases.
|
||||
template <> constexpr inline bool isInt<8>(int64_t x) {
|
||||
return static_cast<int8_t>(x) == x;
|
||||
}
|
||||
template <> constexpr inline bool isInt<16>(int64_t x) {
|
||||
return static_cast<int16_t>(x) == x;
|
||||
}
|
||||
template <> constexpr inline bool isInt<32>(int64_t x) {
|
||||
return static_cast<int32_t>(x) == x;
|
||||
}
|
||||
|
||||
/// Checks if a signed integer is an N bit number shifted left by S.
|
||||
template <unsigned N, unsigned S>
|
||||
constexpr inline bool isShiftedInt(int64_t x) {
|
||||
static_assert(
|
||||
N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number.");
|
||||
static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide.");
|
||||
return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
|
||||
}
|
||||
|
||||
/// Checks if an unsigned integer fits into the given bit width.
|
||||
///
|
||||
/// This is written as two functions rather than as simply
|
||||
///
|
||||
/// return N >= 64 || X < (UINT64_C(1) << N);
|
||||
///
|
||||
/// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting
|
||||
/// left too many places.
|
||||
template <unsigned N>
|
||||
constexpr inline typename std::enable_if<(N < 64), bool>::type
|
||||
isUInt(uint64_t X) {
|
||||
static_assert(N > 0, "isUInt<0> doesn't make sense");
|
||||
return X < (UINT64_C(1) << (N));
|
||||
}
|
||||
template <unsigned N>
|
||||
constexpr inline typename std::enable_if<N >= 64, bool>::type
|
||||
isUInt(uint64_t X) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Template specializations to get better code for common cases.
|
||||
template <> constexpr inline bool isUInt<8>(uint64_t x) {
|
||||
return static_cast<uint8_t>(x) == x;
|
||||
}
|
||||
template <> constexpr inline bool isUInt<16>(uint64_t x) {
|
||||
return static_cast<uint16_t>(x) == x;
|
||||
}
|
||||
template <> constexpr inline bool isUInt<32>(uint64_t x) {
|
||||
return static_cast<uint32_t>(x) == x;
|
||||
}
|
||||
|
||||
/// Checks if a unsigned integer is an N bit number shifted left by S.
|
||||
template <unsigned N, unsigned S>
|
||||
constexpr inline bool isShiftedUInt(uint64_t x) {
|
||||
static_assert(
|
||||
N > 0, "isShiftedUInt<0> doesn't make sense (refers to a 0-bit number)");
|
||||
static_assert(N + S <= 64,
|
||||
"isShiftedUInt<N, S> with N + S > 64 is too wide.");
|
||||
// Per the two static_asserts above, S must be strictly less than 64. So
|
||||
// 1 << S is not undefined behavior.
|
||||
return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
|
||||
}
|
||||
|
||||
/// Gets the maximum value for a N-bit unsigned integer.
|
||||
inline uint64_t maxUIntN(uint64_t N) {
|
||||
assert(N > 0 && N <= 64 && "integer width out of range");
|
||||
|
||||
// uint64_t(1) << 64 is undefined behavior, so we can't do
|
||||
// (uint64_t(1) << N) - 1
|
||||
// without checking first that N != 64. But this works and doesn't have a
|
||||
// branch.
|
||||
return UINT64_MAX >> (64 - N);
|
||||
}
|
||||
|
||||
/// Gets the minimum value for a N-bit signed integer.
|
||||
inline int64_t minIntN(int64_t N) {
|
||||
assert(N > 0 && N <= 64 && "integer width out of range");
|
||||
|
||||
return -(UINT64_C(1)<<(N-1));
|
||||
}
|
||||
|
||||
/// Gets the maximum value for a N-bit signed integer.
|
||||
inline int64_t maxIntN(int64_t N) {
|
||||
assert(N > 0 && N <= 64 && "integer width out of range");
|
||||
|
||||
// This relies on two's complement wraparound when N == 64, so we convert to
|
||||
// int64_t only at the very end to avoid UB.
|
||||
return (UINT64_C(1) << (N - 1)) - 1;
|
||||
}
|
||||
|
||||
/// Checks if an unsigned integer fits into the given (dynamic) bit width.
|
||||
inline bool isUIntN(unsigned N, uint64_t x) {
|
||||
return N >= 64 || x <= maxUIntN(N);
|
||||
}
|
||||
|
||||
/// Checks if an signed integer fits into the given (dynamic) bit width.
|
||||
inline bool isIntN(unsigned N, int64_t x) {
|
||||
return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N));
|
||||
}
|
||||
|
||||
/// Return true if the argument is a non-empty sequence of ones starting at the
|
||||
/// least significant bit with the remainder zero (32 bit version).
|
||||
/// Ex. isMask_32(0x0000FFFFU) == true.
|
||||
constexpr inline bool isMask_32(uint32_t Value) {
|
||||
return Value && ((Value + 1) & Value) == 0;
|
||||
}
|
||||
|
||||
/// Return true if the argument is a non-empty sequence of ones starting at the
|
||||
/// least significant bit with the remainder zero (64 bit version).
|
||||
constexpr inline bool isMask_64(uint64_t Value) {
|
||||
return Value && ((Value + 1) & Value) == 0;
|
||||
}
|
||||
|
||||
/// Return true if the argument contains a non-empty sequence of ones with the
|
||||
/// remainder zero (32 bit version.) Ex. isShiftedMask_32(0x0000FF00U) == true.
|
||||
constexpr inline bool isShiftedMask_32(uint32_t Value) {
|
||||
return Value && isMask_32((Value - 1) | Value);
|
||||
}
|
||||
|
||||
/// Return true if the argument contains a non-empty sequence of ones with the
|
||||
/// remainder zero (64 bit version.)
|
||||
constexpr inline bool isShiftedMask_64(uint64_t Value) {
|
||||
return Value && isMask_64((Value - 1) | Value);
|
||||
}
|
||||
|
||||
/// Return true if the argument is a power of two > 0.
|
||||
/// Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
|
||||
constexpr inline bool isPowerOf2_32(uint32_t Value) {
|
||||
return Value && !(Value & (Value - 1));
|
||||
}
|
||||
|
||||
/// Return true if the argument is a power of two > 0 (64 bit edition.)
|
||||
constexpr inline bool isPowerOf2_64(uint64_t Value) {
|
||||
return Value && !(Value & (Value - 1));
|
||||
}
|
||||
|
||||
/// Return a byte-swapped representation of the 16-bit argument.
|
||||
inline uint16_t ByteSwap_16(uint16_t Value) {
|
||||
return sys::SwapByteOrder_16(Value);
|
||||
}
|
||||
|
||||
/// Return a byte-swapped representation of the 32-bit argument.
|
||||
inline uint32_t ByteSwap_32(uint32_t Value) {
|
||||
return sys::SwapByteOrder_32(Value);
|
||||
}
|
||||
|
||||
/// Return a byte-swapped representation of the 64-bit argument.
|
||||
inline uint64_t ByteSwap_64(uint64_t Value) {
|
||||
return sys::SwapByteOrder_64(Value);
|
||||
}
|
||||
|
||||
/// Count the number of ones from the most significant bit to the first
|
||||
/// zero bit.
|
||||
///
|
||||
/// Ex. countLeadingOnes(0xFF0FFF00) == 8.
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
|
||||
/// ZB_Undefined are valid arguments.
|
||||
template <typename T>
|
||||
unsigned countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
|
||||
static_assert(std::numeric_limits<T>::is_integer &&
|
||||
!std::numeric_limits<T>::is_signed,
|
||||
"Only unsigned integral types are allowed.");
|
||||
return countLeadingZeros<T>(~Value, ZB);
|
||||
}
|
||||
|
||||
/// Count the number of ones from the least significant bit to the first
|
||||
/// zero bit.
|
||||
///
|
||||
/// Ex. countTrailingOnes(0x00FF00FF) == 8.
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// \param ZB the behavior on an input of all ones. Only ZB_Width and
|
||||
/// ZB_Undefined are valid arguments.
|
||||
template <typename T>
|
||||
unsigned countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
|
||||
static_assert(std::numeric_limits<T>::is_integer &&
|
||||
!std::numeric_limits<T>::is_signed,
|
||||
"Only unsigned integral types are allowed.");
|
||||
return countTrailingZeros<T>(~Value, ZB);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename T, std::size_t SizeOfT> struct PopulationCounter {
|
||||
static unsigned count(T Value) {
|
||||
// Generic version, forward to 32 bits.
|
||||
static_assert(SizeOfT <= 4, "Not implemented!");
|
||||
#if defined(__GNUC__)
|
||||
return __builtin_popcount(Value);
|
||||
#else
|
||||
uint32_t v = Value;
|
||||
v = v - ((v >> 1) & 0x55555555);
|
||||
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
|
||||
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct PopulationCounter<T, 8> {
|
||||
static unsigned count(T Value) {
|
||||
#if defined(__GNUC__)
|
||||
return __builtin_popcountll(Value);
|
||||
#else
|
||||
uint64_t v = Value;
|
||||
v = v - ((v >> 1) & 0x5555555555555555ULL);
|
||||
v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
|
||||
v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
|
||||
return unsigned((uint64_t)(v * 0x0101010101010101ULL) >> 56);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// Count the number of set bits in a value.
|
||||
/// Ex. countPopulation(0xF000F000) = 8
|
||||
/// Returns 0 if the word is zero.
|
||||
template <typename T>
|
||||
inline unsigned countPopulation(T Value) {
|
||||
static_assert(std::numeric_limits<T>::is_integer &&
|
||||
!std::numeric_limits<T>::is_signed,
|
||||
"Only unsigned integral types are allowed.");
|
||||
return detail::PopulationCounter<T, sizeof(T)>::count(Value);
|
||||
}
|
||||
|
||||
/// Compile time Log2.
|
||||
/// Valid only for positive powers of two.
|
||||
template <size_t kValue> constexpr inline size_t CTLog2() {
|
||||
static_assert(kValue > 0 && llvm::isPowerOf2_64(kValue),
|
||||
"Value is not a valid power of 2");
|
||||
return 1 + CTLog2<kValue / 2>();
|
||||
}
|
||||
|
||||
template <> constexpr inline size_t CTLog2<1>() { return 0; }
|
||||
|
||||
/// Return the log base 2 of the specified value.
|
||||
inline double Log2(double Value) {
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
|
||||
return __builtin_log(Value) / __builtin_log(2.0);
|
||||
#else
|
||||
return log2(Value);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
|
||||
/// (32 bit edition.)
|
||||
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
|
||||
inline unsigned Log2_32(uint32_t Value) {
|
||||
return 31 - countLeadingZeros(Value);
|
||||
}
|
||||
|
||||
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
|
||||
/// (64 bit edition.)
|
||||
inline unsigned Log2_64(uint64_t Value) {
|
||||
return 63 - countLeadingZeros(Value);
|
||||
}
|
||||
|
||||
/// Return the ceil log base 2 of the specified value, 32 if the value is zero.
|
||||
/// (32 bit edition).
|
||||
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
|
||||
inline unsigned Log2_32_Ceil(uint32_t Value) {
|
||||
return 32 - countLeadingZeros(Value - 1);
|
||||
}
|
||||
|
||||
/// Return the ceil log base 2 of the specified value, 64 if the value is zero.
|
||||
/// (64 bit edition.)
|
||||
inline unsigned Log2_64_Ceil(uint64_t Value) {
|
||||
return 64 - countLeadingZeros(Value - 1);
|
||||
}
|
||||
|
||||
/// Return the greatest common divisor of the values using Euclid's algorithm.
|
||||
template <typename T>
|
||||
inline T greatestCommonDivisor(T A, T B) {
|
||||
while (B) {
|
||||
T Tmp = B;
|
||||
B = A % B;
|
||||
A = Tmp;
|
||||
}
|
||||
return A;
|
||||
}
|
||||
|
||||
inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) {
|
||||
return greatestCommonDivisor<uint64_t>(A, B);
|
||||
}
|
||||
|
||||
/// This function takes a 64-bit integer and returns the bit equivalent double.
|
||||
inline double BitsToDouble(uint64_t Bits) {
|
||||
double D;
|
||||
static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes");
|
||||
memcpy(&D, &Bits, sizeof(Bits));
|
||||
return D;
|
||||
}
|
||||
|
||||
/// This function takes a 32-bit integer and returns the bit equivalent float.
|
||||
inline float BitsToFloat(uint32_t Bits) {
|
||||
float F;
|
||||
static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes");
|
||||
memcpy(&F, &Bits, sizeof(Bits));
|
||||
return F;
|
||||
}
|
||||
|
||||
/// This function takes a double and returns the bit equivalent 64-bit integer.
|
||||
/// Note that copying doubles around changes the bits of NaNs on some hosts,
|
||||
/// notably x86, so this routine cannot be used if these bits are needed.
|
||||
inline uint64_t DoubleToBits(double Double) {
|
||||
uint64_t Bits;
|
||||
static_assert(sizeof(uint64_t) == sizeof(double), "Unexpected type sizes");
|
||||
memcpy(&Bits, &Double, sizeof(Double));
|
||||
return Bits;
|
||||
}
|
||||
|
||||
/// This function takes a float and returns the bit equivalent 32-bit integer.
|
||||
/// Note that copying floats around changes the bits of NaNs on some hosts,
|
||||
/// notably x86, so this routine cannot be used if these bits are needed.
|
||||
inline uint32_t FloatToBits(float Float) {
|
||||
uint32_t Bits;
|
||||
static_assert(sizeof(uint32_t) == sizeof(float), "Unexpected type sizes");
|
||||
memcpy(&Bits, &Float, sizeof(Float));
|
||||
return Bits;
|
||||
}
|
||||
|
||||
/// A and B are either alignments or offsets. Return the minimum alignment that
|
||||
/// may be assumed after adding the two together.
|
||||
constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) {
|
||||
// The largest power of 2 that divides both A and B.
|
||||
//
|
||||
// Replace "-Value" by "1+~Value" in the following commented code to avoid
|
||||
// MSVC warning C4146
|
||||
// return (A | B) & -(A | B);
|
||||
return (A | B) & (1 + ~(A | B));
|
||||
}
|
||||
|
||||
/// Returns the next power of two (in 64-bits) that is strictly greater than A.
|
||||
/// Returns zero on overflow.
|
||||
inline uint64_t NextPowerOf2(uint64_t A) {
|
||||
A |= (A >> 1);
|
||||
A |= (A >> 2);
|
||||
A |= (A >> 4);
|
||||
A |= (A >> 8);
|
||||
A |= (A >> 16);
|
||||
A |= (A >> 32);
|
||||
return A + 1;
|
||||
}
|
||||
|
||||
/// Returns the power of two which is less than or equal to the given value.
|
||||
/// Essentially, it is a floor operation across the domain of powers of two.
|
||||
inline uint64_t PowerOf2Floor(uint64_t A) {
|
||||
if (!A) return 0;
|
||||
return 1ull << (63 - countLeadingZeros(A, ZB_Undefined));
|
||||
}
|
||||
|
||||
/// Returns the power of two which is greater than or equal to the given value.
|
||||
/// Essentially, it is a ceil operation across the domain of powers of two.
|
||||
inline uint64_t PowerOf2Ceil(uint64_t A) {
|
||||
if (!A)
|
||||
return 0;
|
||||
return NextPowerOf2(A - 1);
|
||||
}
|
||||
|
||||
/// Returns the next integer (mod 2**64) that is greater than or equal to
|
||||
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
|
||||
///
|
||||
/// If non-zero \p Skew is specified, the return value will be a minimal
|
||||
/// integer that is greater than or equal to \p Value and equal to
|
||||
/// \p Align * N + \p Skew for some integer N. If \p Skew is larger than
|
||||
/// \p Align, its value is adjusted to '\p Skew mod \p Align'.
|
||||
///
|
||||
/// Examples:
|
||||
/// \code
|
||||
/// alignTo(5, 8) = 8
|
||||
/// alignTo(17, 8) = 24
|
||||
/// alignTo(~0LL, 8) = 0
|
||||
/// alignTo(321, 255) = 510
|
||||
///
|
||||
/// alignTo(5, 8, 7) = 7
|
||||
/// alignTo(17, 8, 1) = 17
|
||||
/// alignTo(~0LL, 8, 3) = 3
|
||||
/// alignTo(321, 255, 42) = 552
|
||||
/// \endcode
|
||||
inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
|
||||
assert(Align != 0u && "Align can't be 0.");
|
||||
Skew %= Align;
|
||||
return (Value + Align - 1 - Skew) / Align * Align + Skew;
|
||||
}
|
||||
|
||||
/// Returns the next integer (mod 2**64) that is greater than or equal to
|
||||
/// \p Value and is a multiple of \c Align. \c Align must be non-zero.
|
||||
template <uint64_t Align> constexpr inline uint64_t alignTo(uint64_t Value) {
|
||||
static_assert(Align != 0u, "Align must be non-zero");
|
||||
return (Value + Align - 1) / Align * Align;
|
||||
}
|
||||
|
||||
/// Returns the integer ceil(Numerator / Denominator).
|
||||
inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) {
|
||||
return alignTo(Numerator, Denominator) / Denominator;
|
||||
}
|
||||
|
||||
/// Returns the largest uint64_t less than or equal to \p Value and is
|
||||
/// \p Skew mod \p Align. \p Align must be non-zero
|
||||
inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
|
||||
assert(Align != 0u && "Align can't be 0.");
|
||||
Skew %= Align;
|
||||
return (Value - Skew) / Align * Align + Skew;
|
||||
}
|
||||
|
||||
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
|
||||
/// Requires 0 < B <= 32.
|
||||
template <unsigned B> constexpr inline int32_t SignExtend32(uint32_t X) {
|
||||
static_assert(B > 0, "Bit width can't be 0.");
|
||||
static_assert(B <= 32, "Bit width out of range.");
|
||||
return int32_t(X << (32 - B)) >> (32 - B);
|
||||
}
|
||||
|
||||
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
|
||||
/// Requires 0 < B < 32.
|
||||
inline int32_t SignExtend32(uint32_t X, unsigned B) {
|
||||
assert(B > 0 && "Bit width can't be 0.");
|
||||
assert(B <= 32 && "Bit width out of range.");
|
||||
return int32_t(X << (32 - B)) >> (32 - B);
|
||||
}
|
||||
|
||||
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
|
||||
/// Requires 0 < B < 64.
|
||||
template <unsigned B> constexpr inline int64_t SignExtend64(uint64_t x) {
|
||||
static_assert(B > 0, "Bit width can't be 0.");
|
||||
static_assert(B <= 64, "Bit width out of range.");
|
||||
return int64_t(x << (64 - B)) >> (64 - B);
|
||||
}
|
||||
|
||||
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
|
||||
/// Requires 0 < B < 64.
|
||||
inline int64_t SignExtend64(uint64_t X, unsigned B) {
|
||||
assert(B > 0 && "Bit width can't be 0.");
|
||||
assert(B <= 64 && "Bit width out of range.");
|
||||
return int64_t(X << (64 - B)) >> (64 - B);
|
||||
}
|
||||
|
||||
/// Subtract two unsigned integers, X and Y, of type T and return the absolute
|
||||
/// value of the result.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_unsigned<T>::value, T>::type
|
||||
AbsoluteDifference(T X, T Y) {
|
||||
return std::max(X, Y) - std::min(X, Y);
|
||||
}
|
||||
|
||||
/// Add two unsigned integers, X and Y, of type T. Clamp the result to the
|
||||
/// maximum representable value of T on overflow. ResultOverflowed indicates if
|
||||
/// the result is larger than the maximum representable value of type T.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_unsigned<T>::value, T>::type
|
||||
SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
|
||||
bool Dummy;
|
||||
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
|
||||
// Hacker's Delight, p. 29
|
||||
T Z = X + Y;
|
||||
Overflowed = (Z < X || Z < Y);
|
||||
if (Overflowed)
|
||||
return std::numeric_limits<T>::max();
|
||||
else
|
||||
return Z;
|
||||
}
|
||||
|
||||
/// Multiply two unsigned integers, X and Y, of type T. Clamp the result to the
|
||||
/// maximum representable value of T on overflow. ResultOverflowed indicates if
|
||||
/// the result is larger than the maximum representable value of type T.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_unsigned<T>::value, T>::type
|
||||
SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
|
||||
bool Dummy;
|
||||
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
|
||||
|
||||
// Hacker's Delight, p. 30 has a different algorithm, but we don't use that
|
||||
// because it fails for uint16_t (where multiplication can have undefined
|
||||
// behavior due to promotion to int), and requires a division in addition
|
||||
// to the multiplication.
|
||||
|
||||
Overflowed = false;
|
||||
|
||||
// Log2(Z) would be either Log2Z or Log2Z + 1.
|
||||
// Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z
|
||||
// will necessarily be less than Log2Max as desired.
|
||||
int Log2Z = Log2_64(X) + Log2_64(Y);
|
||||
const T Max = std::numeric_limits<T>::max();
|
||||
int Log2Max = Log2_64(Max);
|
||||
if (Log2Z < Log2Max) {
|
||||
return X * Y;
|
||||
}
|
||||
if (Log2Z > Log2Max) {
|
||||
Overflowed = true;
|
||||
return Max;
|
||||
}
|
||||
|
||||
// We're going to use the top bit, and maybe overflow one
|
||||
// bit past it. Multiply all but the bottom bit then add
|
||||
// that on at the end.
|
||||
T Z = (X >> 1) * Y;
|
||||
if (Z & ~(Max >> 1)) {
|
||||
Overflowed = true;
|
||||
return Max;
|
||||
}
|
||||
Z <<= 1;
|
||||
if (X & 1)
|
||||
return SaturatingAdd(Z, Y, ResultOverflowed);
|
||||
|
||||
return Z;
|
||||
}
|
||||
|
||||
/// Multiply two unsigned integers, X and Y, and add the unsigned integer, A to
|
||||
/// the product. Clamp the result to the maximum representable value of T on
|
||||
/// overflow. ResultOverflowed indicates if the result is larger than the
|
||||
/// maximum representable value of type T.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_unsigned<T>::value, T>::type
|
||||
SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
|
||||
bool Dummy;
|
||||
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
|
||||
|
||||
T Product = SaturatingMultiply(X, Y, &Overflowed);
|
||||
if (Overflowed)
|
||||
return Product;
|
||||
|
||||
return SaturatingAdd(A, Product, &Overflowed);
|
||||
}
|
||||
|
||||
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
|
||||
extern const float huge_valf;
|
||||
|
||||
|
||||
/// Add two signed integers, computing the two's complement truncated result,
|
||||
/// returning true if overflow occured.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_signed<T>::value, T>::type
|
||||
AddOverflow(T X, T Y, T &Result) {
|
||||
#if __has_builtin(__builtin_add_overflow)
|
||||
return __builtin_add_overflow(X, Y, &Result);
|
||||
#else
|
||||
// Perform the unsigned addition.
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
const U UX = static_cast<U>(X);
|
||||
const U UY = static_cast<U>(Y);
|
||||
const U UResult = UX + UY;
|
||||
|
||||
// Convert to signed.
|
||||
Result = static_cast<T>(UResult);
|
||||
|
||||
// Adding two positive numbers should result in a positive number.
|
||||
if (X > 0 && Y > 0)
|
||||
return Result <= 0;
|
||||
// Adding two negatives should result in a negative number.
|
||||
if (X < 0 && Y < 0)
|
||||
return Result >= 0;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Subtract two signed integers, computing the two's complement truncated
|
||||
/// result, returning true if an overflow ocurred.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_signed<T>::value, T>::type
|
||||
SubOverflow(T X, T Y, T &Result) {
|
||||
#if __has_builtin(__builtin_sub_overflow)
|
||||
return __builtin_sub_overflow(X, Y, &Result);
|
||||
#else
|
||||
// Perform the unsigned addition.
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
const U UX = static_cast<U>(X);
|
||||
const U UY = static_cast<U>(Y);
|
||||
const U UResult = UX - UY;
|
||||
|
||||
// Convert to signed.
|
||||
Result = static_cast<T>(UResult);
|
||||
|
||||
// Subtracting a positive number from a negative results in a negative number.
|
||||
if (X <= 0 && Y > 0)
|
||||
return Result >= 0;
|
||||
// Subtracting a negative number from a positive results in a positive number.
|
||||
if (X >= 0 && Y < 0)
|
||||
return Result <= 0;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Multiply two signed integers, computing the two's complement truncated
|
||||
/// result, returning true if an overflow ocurred.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_signed<T>::value, T>::type
|
||||
MulOverflow(T X, T Y, T &Result) {
|
||||
// Perform the unsigned multiplication on absolute values.
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
const U UX = X < 0 ? (0 - static_cast<U>(X)) : static_cast<U>(X);
|
||||
const U UY = Y < 0 ? (0 - static_cast<U>(Y)) : static_cast<U>(Y);
|
||||
const U UResult = UX * UY;
|
||||
|
||||
// Convert to signed.
|
||||
const bool IsNegative = (X < 0) ^ (Y < 0);
|
||||
Result = IsNegative ? (0 - UResult) : UResult;
|
||||
|
||||
// If any of the args was 0, result is 0 and no overflow occurs.
|
||||
if (UX == 0 || UY == 0)
|
||||
return false;
|
||||
|
||||
// UX and UY are in [1, 2^n], where n is the number of digits.
|
||||
// Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
|
||||
// positive) divided by an argument compares to the other.
|
||||
if (IsNegative)
|
||||
return UX > (static_cast<U>(std::numeric_limits<T>::max()) + U(1)) / UY;
|
||||
else
|
||||
return UX > (static_cast<U>(std::numeric_limits<T>::max())) / UY;
|
||||
}
|
||||
|
||||
} // End llvm namespace
|
||||
} // End opencombine namespace
|
||||
|
||||
#endif
|
||||
@@ -1,166 +0,0 @@
|
||||
//===- SwapByteOrder.h - Generic and optimized byte swaps -------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares generic and optimized functions to swap the byte order of
|
||||
// an integral type.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_SUPPORT_SWAPBYTEORDER_H
|
||||
#define OPENCOMBINE_LLVM_SUPPORT_SWAPBYTEORDER_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/DataTypes.h"
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#if defined(_MSC_VER) && !defined(_DEBUG)
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__)
|
||||
#include <endian.h>
|
||||
#elif defined(_AIX)
|
||||
#include <sys/machine.h>
|
||||
#elif defined(__sun)
|
||||
/* Solaris provides _BIG_ENDIAN/_LITTLE_ENDIAN selector in sys/types.h */
|
||||
#include <sys/types.h>
|
||||
#define BIG_ENDIAN 4321
|
||||
#define LITTLE_ENDIAN 1234
|
||||
#if defined(_BIG_ENDIAN)
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#else
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#endif
|
||||
#else
|
||||
#if !defined(BYTE_ORDER) && !defined(_WIN32)
|
||||
#include <machine/endian.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
namespace sys {
|
||||
|
||||
#if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN
|
||||
constexpr bool IsBigEndianHost = true;
|
||||
#else
|
||||
constexpr bool IsBigEndianHost = false;
|
||||
#endif
|
||||
|
||||
static const bool IsLittleEndianHost = !IsBigEndianHost;
|
||||
|
||||
/// SwapByteOrder_16 - This function returns a byte-swapped representation of
|
||||
/// the 16-bit argument.
|
||||
inline uint16_t SwapByteOrder_16(uint16_t value) {
|
||||
#if defined(_MSC_VER) && !defined(_DEBUG)
|
||||
// The DLL version of the runtime lacks these functions (bug!?), but in a
|
||||
// release build they're replaced with BSWAP instructions anyway.
|
||||
return _byteswap_ushort(value);
|
||||
#else
|
||||
uint16_t Hi = value << 8;
|
||||
uint16_t Lo = value >> 8;
|
||||
return Hi | Lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// This function returns a byte-swapped representation of the 32-bit argument.
|
||||
inline uint32_t SwapByteOrder_32(uint32_t value) {
|
||||
#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC))
|
||||
return __builtin_bswap32(value);
|
||||
#elif defined(_MSC_VER) && !defined(_DEBUG)
|
||||
return _byteswap_ulong(value);
|
||||
#else
|
||||
uint32_t Byte0 = value & 0x000000FF;
|
||||
uint32_t Byte1 = value & 0x0000FF00;
|
||||
uint32_t Byte2 = value & 0x00FF0000;
|
||||
uint32_t Byte3 = value & 0xFF000000;
|
||||
return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// This function returns a byte-swapped representation of the 64-bit argument.
|
||||
inline uint64_t SwapByteOrder_64(uint64_t value) {
|
||||
#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC))
|
||||
return __builtin_bswap64(value);
|
||||
#elif defined(_MSC_VER) && !defined(_DEBUG)
|
||||
return _byteswap_uint64(value);
|
||||
#else
|
||||
uint64_t Hi = SwapByteOrder_32(uint32_t(value));
|
||||
uint32_t Lo = SwapByteOrder_32(uint32_t(value >> 32));
|
||||
return (Hi << 32) | Lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline unsigned char getSwappedBytes(unsigned char C) { return C; }
|
||||
inline signed char getSwappedBytes(signed char C) { return C; }
|
||||
inline char getSwappedBytes(char C) { return C; }
|
||||
|
||||
inline unsigned short getSwappedBytes(unsigned short C) { return SwapByteOrder_16(C); }
|
||||
inline signed short getSwappedBytes( signed short C) { return SwapByteOrder_16(C); }
|
||||
|
||||
inline unsigned int getSwappedBytes(unsigned int C) { return SwapByteOrder_32(C); }
|
||||
inline signed int getSwappedBytes( signed int C) { return SwapByteOrder_32(C); }
|
||||
|
||||
#if __LONG_MAX__ == __INT_MAX__
|
||||
inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_32(C); }
|
||||
inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_32(C); }
|
||||
#elif __LONG_MAX__ == __LONG_LONG_MAX__
|
||||
inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_64(C); }
|
||||
inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_64(C); }
|
||||
#else
|
||||
#error "Unknown long size!"
|
||||
#endif
|
||||
|
||||
inline unsigned long long getSwappedBytes(unsigned long long C) {
|
||||
return SwapByteOrder_64(C);
|
||||
}
|
||||
inline signed long long getSwappedBytes(signed long long C) {
|
||||
return SwapByteOrder_64(C);
|
||||
}
|
||||
|
||||
inline float getSwappedBytes(float C) {
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} in, out;
|
||||
in.f = C;
|
||||
out.i = SwapByteOrder_32(in.i);
|
||||
return out.f;
|
||||
}
|
||||
|
||||
inline double getSwappedBytes(double C) {
|
||||
union {
|
||||
uint64_t i;
|
||||
double d;
|
||||
} in, out;
|
||||
in.d = C;
|
||||
out.i = SwapByteOrder_64(in.i);
|
||||
return out.d;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline typename std::enable_if<std::is_enum<T>::value, T>::type
|
||||
getSwappedBytes(T C) {
|
||||
return static_cast<T>(
|
||||
getSwappedBytes(static_cast<typename std::underlying_type<T>::type>(C)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void swapByteOrder(T &Value) {
|
||||
Value = getSwappedBytes(Value);
|
||||
}
|
||||
|
||||
} // end namespace sys
|
||||
} // end namespace llvm
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif
|
||||
@@ -1,198 +0,0 @@
|
||||
//===- llvm/Support/type_traits.h - Simplfied type traits -------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides useful additions to the standard type_traits library.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The llvm namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_LLVM_SUPPORT_TYPE_TRAITS_H
|
||||
#define OPENCOMBINE_LLVM_SUPPORT_TYPE_TRAITS_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace opencombine {
|
||||
namespace llvm {
|
||||
|
||||
|
||||
/// Metafunction that determines whether the given type is either an
|
||||
/// integral type or an enumeration type, including enum classes.
|
||||
///
|
||||
/// Note that this accepts potentially more integral types than is_integral
|
||||
/// because it is based on being implicitly convertible to an integral type.
|
||||
/// Also note that enum classes aren't implicitly convertible to integral types,
|
||||
/// the value may therefore need to be explicitly converted before being used.
|
||||
template <typename T> class is_integral_or_enum {
|
||||
using UnderlyingT = typename std::remove_reference<T>::type;
|
||||
|
||||
public:
|
||||
static const bool value =
|
||||
!std::is_class<UnderlyingT>::value && // Filter conversion operators.
|
||||
!std::is_pointer<UnderlyingT>::value &&
|
||||
!std::is_floating_point<UnderlyingT>::value &&
|
||||
(std::is_enum<UnderlyingT>::value ||
|
||||
std::is_convertible<UnderlyingT, unsigned long long>::value);
|
||||
};
|
||||
|
||||
/// If T is a pointer, just return it. If it is not, return T&.
|
||||
template<typename T, typename Enable = void>
|
||||
struct add_lvalue_reference_if_not_pointer { using type = T &; };
|
||||
|
||||
template <typename T>
|
||||
struct add_lvalue_reference_if_not_pointer<
|
||||
T, typename std::enable_if<std::is_pointer<T>::value>::type> {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
/// If T is a pointer to X, return a pointer to const X. If it is not,
|
||||
/// return const T.
|
||||
template<typename T, typename Enable = void>
|
||||
struct add_const_past_pointer { using type = const T; };
|
||||
|
||||
template <typename T>
|
||||
struct add_const_past_pointer<
|
||||
T, typename std::enable_if<std::is_pointer<T>::value>::type> {
|
||||
using type = const typename std::remove_pointer<T>::type *;
|
||||
};
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct const_pointer_or_const_ref {
|
||||
using type = const T &;
|
||||
};
|
||||
template <typename T>
|
||||
struct const_pointer_or_const_ref<
|
||||
T, typename std::enable_if<std::is_pointer<T>::value>::type> {
|
||||
using type = typename add_const_past_pointer<T>::type;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
/// Internal utility to detect trivial copy construction.
|
||||
template<typename T> union copy_construction_triviality_helper {
|
||||
T t;
|
||||
copy_construction_triviality_helper() = default;
|
||||
copy_construction_triviality_helper(const copy_construction_triviality_helper&) = default;
|
||||
~copy_construction_triviality_helper() = default;
|
||||
};
|
||||
/// Internal utility to detect trivial move construction.
|
||||
template<typename T> union move_construction_triviality_helper {
|
||||
T t;
|
||||
move_construction_triviality_helper() = default;
|
||||
move_construction_triviality_helper(move_construction_triviality_helper&&) = default;
|
||||
~move_construction_triviality_helper() = default;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
union trivial_helper {
|
||||
T t;
|
||||
};
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/// An implementation of `std::is_trivially_copy_constructible` since we have
|
||||
/// users with STLs that don't yet include it.
|
||||
template <typename T>
|
||||
struct is_trivially_copy_constructible
|
||||
: std::is_copy_constructible<
|
||||
::opencombine::llvm::detail::copy_construction_triviality_helper<T>> {};
|
||||
template <typename T>
|
||||
struct is_trivially_copy_constructible<T &> : std::true_type {};
|
||||
template <typename T>
|
||||
struct is_trivially_copy_constructible<T &&> : std::false_type {};
|
||||
|
||||
/// An implementation of `std::is_trivially_move_constructible` since we have
|
||||
/// users with STLs that don't yet include it.
|
||||
template <typename T>
|
||||
struct is_trivially_move_constructible
|
||||
: std::is_move_constructible<
|
||||
::opencombine::llvm::detail::move_construction_triviality_helper<T>> {};
|
||||
template <typename T>
|
||||
struct is_trivially_move_constructible<T &> : std::true_type {};
|
||||
template <typename T>
|
||||
struct is_trivially_move_constructible<T &&> : std::true_type {};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct is_copy_assignable {
|
||||
template<class F>
|
||||
static auto get(F*) -> decltype(std::declval<F &>() = std::declval<const F &>(), std::true_type{});
|
||||
static std::false_type get(...);
|
||||
static constexpr bool value = decltype(get((T*)nullptr))::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_move_assignable {
|
||||
template<class F>
|
||||
static auto get(F*) -> decltype(std::declval<F &>() = std::declval<F &&>(), std::true_type{});
|
||||
static std::false_type get(...);
|
||||
static constexpr bool value = decltype(get((T*)nullptr))::value;
|
||||
};
|
||||
|
||||
|
||||
// An implementation of `std::is_trivially_copyable` since STL version
|
||||
// is not equally supported by all compilers, especially GCC 4.9.
|
||||
// Uniform implementation of this trait is important for ABI compatibility
|
||||
// as it has an impact on SmallVector's ABI (among others).
|
||||
template <typename T>
|
||||
class is_trivially_copyable {
|
||||
|
||||
// copy constructors
|
||||
static constexpr bool has_trivial_copy_constructor =
|
||||
std::is_copy_constructible<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_copy_constructor =
|
||||
!std::is_copy_constructible<T>::value;
|
||||
|
||||
// move constructors
|
||||
static constexpr bool has_trivial_move_constructor =
|
||||
std::is_move_constructible<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_move_constructor =
|
||||
!std::is_move_constructible<T>::value;
|
||||
|
||||
// copy assign
|
||||
static constexpr bool has_trivial_copy_assign =
|
||||
is_copy_assignable<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_copy_assign =
|
||||
!is_copy_assignable<T>::value;
|
||||
|
||||
// move assign
|
||||
static constexpr bool has_trivial_move_assign =
|
||||
is_move_assignable<detail::trivial_helper<T>>::value;
|
||||
static constexpr bool has_deleted_move_assign =
|
||||
!is_move_assignable<T>::value;
|
||||
|
||||
// destructor
|
||||
static constexpr bool has_trivial_destructor =
|
||||
std::is_destructible<detail::trivial_helper<T>>::value;
|
||||
|
||||
public:
|
||||
|
||||
static constexpr bool value =
|
||||
has_trivial_destructor &&
|
||||
(has_deleted_move_assign || has_trivial_move_assign) &&
|
||||
(has_deleted_move_constructor || has_trivial_move_constructor) &&
|
||||
(has_deleted_copy_assign || has_trivial_copy_assign) &&
|
||||
(has_deleted_copy_constructor || has_trivial_copy_constructor);
|
||||
|
||||
#ifdef HAVE_STD_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(value == std::is_trivially_copyable<T>::value,
|
||||
"inconsistent behavior between llvm:: and std:: implementation of is_trivially_copyable");
|
||||
#endif
|
||||
};
|
||||
template <typename T>
|
||||
class is_trivially_copyable<T*> : public std::true_type {
|
||||
};
|
||||
|
||||
|
||||
} // end namespace llvm
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_LLVM_SUPPORT_TYPE_TRAITS_H
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// span.h
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 30.10.2019.
|
||||
//
|
||||
|
||||
#ifndef OPENCOMBINE_SPAN_H
|
||||
#define OPENCOMBINE_SPAN_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <iterator>
|
||||
|
||||
namespace opencombine {
|
||||
|
||||
// This is a very simplified implementation of std::span from C++20.
|
||||
// Not all compilers support it yet.
|
||||
template <typename T>
|
||||
class span {
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv<T>;
|
||||
using index_type = size_t;
|
||||
using differenc_type = ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
constexpr span() noexcept: data_(nullptr), size_(0) {}
|
||||
constexpr span(pointer ptr, index_type count): data_(ptr), size_(count) {}
|
||||
constexpr span(pointer first, pointer last): span(first, last - first) {}
|
||||
constexpr span(const span& other) noexcept = default;
|
||||
|
||||
constexpr span& operator=(const span& other) noexcept = default;
|
||||
|
||||
constexpr const_reference operator[](index_type index) const noexcept {
|
||||
assert(index < size_);
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
constexpr reference operator[](index_type index) noexcept {
|
||||
assert(index < size_);
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
const_pointer data() const noexcept { return data_; }
|
||||
|
||||
pointer data() noexcept { return data_; }
|
||||
|
||||
index_type size() const noexcept { return size_; }
|
||||
|
||||
const_iterator begin() const noexcept { return data_; }
|
||||
iterator begin() noexcept { return data_; }
|
||||
|
||||
const_iterator end() const noexcept { return data_ + size_; }
|
||||
iterator end() noexcept { return data_ + size_; }
|
||||
|
||||
bool operator==(const span& other) const {
|
||||
return size_ == other.size_ && std::equal(begin(), end(), other.begin());
|
||||
}
|
||||
|
||||
bool operator!=(const span& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
private:
|
||||
pointer data_;
|
||||
index_type size_;
|
||||
};
|
||||
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif /* OPENCOMBINE_SPAN_H */
|
||||
@@ -1,56 +0,0 @@
|
||||
//
|
||||
// string_view.h
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 31.10.2019.
|
||||
//
|
||||
|
||||
#ifndef OPENCOMBINE_STRING_VIEW_H
|
||||
#define OPENCOMBINE_STRING_VIEW_H
|
||||
|
||||
#include <iterator>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
namespace opencombine {
|
||||
|
||||
// This is a very simplified implementation of std::string_view from C++17.
|
||||
// Not all compilers support it yet.
|
||||
struct string_view {
|
||||
using traits_type = void;
|
||||
using value_type = char;
|
||||
using pointer = char*;
|
||||
using const_pointer = const char*;
|
||||
using reference = char&;
|
||||
using const_reference = const char&;
|
||||
using const_iterator = const char*;
|
||||
using iterator = const_iterator;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using reverse_iterator = const_reverse_iterator;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
string_view() noexcept : string_view(nullptr, 0) {}
|
||||
string_view(const string_view& other) noexcept = default;
|
||||
string_view(const char* data, size_type size) : data_(data), size_(size) {}
|
||||
string_view(const char* data) : string_view(data, strlen(data)) {}
|
||||
|
||||
string_view& operator=(const string_view& view) noexcept = default;
|
||||
|
||||
const_pointer data() const noexcept { return data_; }
|
||||
|
||||
constexpr size_type size() const noexcept { return size_; }
|
||||
|
||||
const_iterator begin() const noexcept { return data_; }
|
||||
iterator begin() noexcept { return data_; }
|
||||
|
||||
const_iterator end() const noexcept { return data_ + size_; }
|
||||
iterator end() noexcept { return data_ + size_; }
|
||||
private:
|
||||
const_pointer data_;
|
||||
size_type size_;
|
||||
};
|
||||
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif /* OPENCOMBINE_STRING_VIEW_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,93 +0,0 @@
|
||||
//===--- MetadataKind.def ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a file that enables metaprogramming with metadata kinds.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// METADATAKIND(Name, Value)
|
||||
/// Represents a swift native runtime metadata kind. Name is the Name of the
|
||||
/// metadata kind and Value is the integral value used to identify the value.
|
||||
#ifndef METADATAKIND
|
||||
#define METADATAKIND(Name, Value)
|
||||
#endif
|
||||
|
||||
/// ABSTRACTMETADATAKIND(Name, Start, End)
|
||||
/// Represents an abstraction categorization of a range of metadata kind
|
||||
/// values. Name is the identifier of the range and Start, End are the
|
||||
/// beginning and end of the range.
|
||||
#ifndef ABSTRACTMETADATAKIND
|
||||
#define ABSTRACTMETADATAKIND(Name, Start, End)
|
||||
#endif
|
||||
|
||||
/// NOMINALTYPEMETADATAKIND(Name, Value)
|
||||
/// Represents the native metadata kind for a swift nominal type. Name is the
|
||||
/// name of the kind and Value is the integral value used to identify the
|
||||
/// value. Delegates to METADATAKIND if not defined.
|
||||
#ifndef NOMINALTYPEMETADATAKIND
|
||||
#define NOMINALTYPEMETADATAKIND(Name, Value) METADATAKIND(Name, Value)
|
||||
#endif
|
||||
|
||||
/// A class type.
|
||||
NOMINALTYPEMETADATAKIND(Class, 0)
|
||||
|
||||
/// A struct type.
|
||||
NOMINALTYPEMETADATAKIND(Struct, 0 | MetadataKindIsNonHeap)
|
||||
|
||||
/// An enum type.
|
||||
/// If we add reference enums, that needs to go here.
|
||||
NOMINALTYPEMETADATAKIND(Enum, 1 | MetadataKindIsNonHeap)
|
||||
|
||||
/// An optional type.
|
||||
NOMINALTYPEMETADATAKIND(Optional, 2 | MetadataKindIsNonHeap)
|
||||
|
||||
/// A foreign class, such as a Core Foundation class.
|
||||
METADATAKIND(ForeignClass, 3 | MetadataKindIsNonHeap)
|
||||
|
||||
/// A type whose value is not exposed in the metadata system.
|
||||
METADATAKIND(Opaque, 0 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
|
||||
|
||||
/// A tuple.
|
||||
METADATAKIND(Tuple, 1 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
|
||||
|
||||
/// A monomorphic function.
|
||||
METADATAKIND(Function, 2 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
|
||||
|
||||
/// An existential type.
|
||||
METADATAKIND(Existential, 3 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
|
||||
|
||||
/// A metatype.
|
||||
METADATAKIND(Metatype, 4 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
|
||||
|
||||
/// An ObjC class wrapper.
|
||||
METADATAKIND(ObjCClassWrapper, 5 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
|
||||
|
||||
/// An existential metatype.
|
||||
METADATAKIND(ExistentialMetatype, 6 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
|
||||
|
||||
/// A heap-allocated local variable using statically-generated metadata.
|
||||
METADATAKIND(HeapLocalVariable, 0 | MetadataKindIsNonType)
|
||||
|
||||
/// A heap-allocated local variable using runtime-instantiated metadata.
|
||||
METADATAKIND(HeapGenericLocalVariable,
|
||||
0 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
|
||||
|
||||
/// A native error object.
|
||||
METADATAKIND(ErrorObject,
|
||||
1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
|
||||
|
||||
// getEnumeratedMetadataKind assumes that all the enumerated values here
|
||||
// will be <= LastEnumeratedMetadataKind.
|
||||
|
||||
#undef ABSTRACTMETADATAKIND
|
||||
#undef NOMINALTYPEMETADATAKIND
|
||||
#undef METADATAKIND
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,395 +0,0 @@
|
||||
//===--- TrailingObjects.h - Variable-length, Swift-ABI classes -*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This header is a fork of the LLVM TrailingObjects template. It has the
|
||||
/// additional constraint over llvm::TrailingObjects of having to maintain ABI
|
||||
/// stability across versions. The following documentation is copied from
|
||||
/// the original TrailingObjects implementation:
|
||||
///
|
||||
/// This header defines support for implementing classes that have
|
||||
/// some trailing object (or arrays of objects) appended to them. The
|
||||
/// main purpose is to make it obvious where this idiom is being used,
|
||||
/// and to make the usage more idiomatic and more difficult to get
|
||||
/// wrong.
|
||||
///
|
||||
/// The TrailingObject template abstracts away the reinterpret_cast,
|
||||
/// pointer arithmetic, and size calculations used for the allocation
|
||||
/// and access of appended arrays of objects, and takes care that they
|
||||
/// are all allocated at their required alignment. Additionally, it
|
||||
/// ensures that the base type is final -- deriving from a class that
|
||||
/// expects data appended immediately after it is typically not safe.
|
||||
///
|
||||
/// Users are expected to derive from this template, and provide
|
||||
/// numTrailingObjects implementations for each trailing type except
|
||||
/// the last, e.g. like this sample:
|
||||
///
|
||||
/// \code
|
||||
/// class VarLengthObj : private TrailingObjects<VarLengthObj, int, double> {
|
||||
/// friend TrailingObjects;
|
||||
///
|
||||
/// unsigned NumInts, NumDoubles;
|
||||
/// size_t numTrailingObjects(OverloadToken<int>) const { return NumInts; }
|
||||
/// };
|
||||
/// \endcode
|
||||
///
|
||||
/// You can access the appended arrays via 'getTrailingObjects', and
|
||||
/// determine the size needed for allocation via
|
||||
/// 'additionalSizeToAlloc' and 'totalSizeToAlloc'.
|
||||
///
|
||||
/// All the methods implemented by this class are are intended for use
|
||||
/// by the implementation of the class, not as part of its interface
|
||||
/// (thus, private inheritance is suggested).
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef OPENCOMBINE_TRAILINGOBJECTS_H
|
||||
#define OPENCOMBINE_TRAILINGOBJECTS_H
|
||||
|
||||
#include "llvm/Support/AlignOf.h"
|
||||
#include "llvm/Support/Alignment.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/type_traits.h"
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
namespace opencombine {
|
||||
namespace swift {
|
||||
namespace ABI {
|
||||
|
||||
namespace trailing_objects_internal {
|
||||
/// Helper template to calculate the max alignment requirement for a set of
|
||||
/// objects.
|
||||
template <typename First, typename... Rest> class AlignmentCalcHelper {
|
||||
private:
|
||||
enum {
|
||||
FirstAlignment = alignof(First),
|
||||
RestAlignment = AlignmentCalcHelper<Rest...>::Alignment,
|
||||
};
|
||||
|
||||
public:
|
||||
enum {
|
||||
Alignment = FirstAlignment > RestAlignment ? FirstAlignment : RestAlignment
|
||||
};
|
||||
};
|
||||
|
||||
template <typename First> class AlignmentCalcHelper<First> {
|
||||
public:
|
||||
enum { Alignment = alignof(First) };
|
||||
};
|
||||
|
||||
/// The base class for TrailingObjects* classes.
|
||||
class TrailingObjectsBase {
|
||||
protected:
|
||||
/// OverloadToken's purpose is to allow specifying function overloads
|
||||
/// for different types, without actually taking the types as
|
||||
/// parameters. (Necessary because member function templates cannot
|
||||
/// be specialized, so overloads must be used instead of
|
||||
/// specialization.)
|
||||
template <typename T> struct OverloadToken {};
|
||||
};
|
||||
|
||||
template <int Align>
|
||||
class TrailingObjectsAligner : public TrailingObjectsBase {};
|
||||
template <>
|
||||
class alignas(1) TrailingObjectsAligner<1> : public TrailingObjectsBase {};
|
||||
template <>
|
||||
class alignas(2) TrailingObjectsAligner<2> : public TrailingObjectsBase {};
|
||||
template <>
|
||||
class alignas(4) TrailingObjectsAligner<4> : public TrailingObjectsBase {};
|
||||
template <>
|
||||
class alignas(8) TrailingObjectsAligner<8> : public TrailingObjectsBase {};
|
||||
template <>
|
||||
class alignas(16) TrailingObjectsAligner<16> : public TrailingObjectsBase {};
|
||||
template <>
|
||||
class alignas(32) TrailingObjectsAligner<32> : public TrailingObjectsBase {};
|
||||
|
||||
// Just a little helper for transforming a type pack into the same
|
||||
// number of a different type. e.g.:
|
||||
// ExtractSecondType<Foo..., int>::type
|
||||
template <typename Ty1, typename Ty2> struct ExtractSecondType {
|
||||
typedef Ty2 type;
|
||||
};
|
||||
|
||||
// TrailingObjectsImpl is somewhat complicated, because it is a
|
||||
// recursively inheriting template, in order to handle the template
|
||||
// varargs. Each level of inheritance picks off a single trailing type
|
||||
// then recurses on the rest. The "Align", "BaseTy", and
|
||||
// "TopTrailingObj" arguments are passed through unchanged through the
|
||||
// recursion. "PrevTy" is, at each level, the type handled by the
|
||||
// level right above it.
|
||||
|
||||
template <int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy,
|
||||
typename... MoreTys>
|
||||
class TrailingObjectsImpl {
|
||||
// The main template definition is never used -- the two
|
||||
// specializations cover all possibilities.
|
||||
};
|
||||
|
||||
template <int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy,
|
||||
typename NextTy, typename... MoreTys>
|
||||
class TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, PrevTy, NextTy,
|
||||
MoreTys...>
|
||||
: public TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, NextTy,
|
||||
MoreTys...> {
|
||||
|
||||
typedef TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, NextTy, MoreTys...>
|
||||
ParentType;
|
||||
|
||||
struct RequiresRealignment {
|
||||
static const bool value = alignof(PrevTy) < alignof(NextTy);
|
||||
};
|
||||
|
||||
static constexpr bool requiresRealignment() {
|
||||
return RequiresRealignment::value;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Ensure the inherited getTrailingObjectsImpl is not hidden.
|
||||
using ParentType::getTrailingObjectsImpl;
|
||||
|
||||
// These two functions are helper functions for
|
||||
// TrailingObjects::getTrailingObjects. They recurse to the left --
|
||||
// the result for each type in the list of trailing types depends on
|
||||
// the result of calling the function on the type to the
|
||||
// left. However, the function for the type to the left is
|
||||
// implemented by a *subclass* of this class, so we invoke it via
|
||||
// the TopTrailingObj, which is, via the
|
||||
// curiously-recurring-template-pattern, the most-derived type in
|
||||
// this recursion, and thus, contains all the overloads.
|
||||
static const NextTy *
|
||||
getTrailingObjectsImpl(const BaseTy *Obj,
|
||||
TrailingObjectsBase::OverloadToken<NextTy>) {
|
||||
auto *Ptr = TopTrailingObj::getTrailingObjectsImpl(
|
||||
Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) +
|
||||
TopTrailingObj::callNumTrailingObjects(
|
||||
Obj, TrailingObjectsBase::OverloadToken<PrevTy>());
|
||||
|
||||
if (requiresRealignment())
|
||||
return reinterpret_cast<const NextTy *>(
|
||||
llvm::alignAddr(Ptr, llvm::Align(alignof(NextTy))));
|
||||
else
|
||||
return reinterpret_cast<const NextTy *>(Ptr);
|
||||
}
|
||||
|
||||
static NextTy *
|
||||
getTrailingObjectsImpl(BaseTy *Obj,
|
||||
TrailingObjectsBase::OverloadToken<NextTy>) {
|
||||
auto *Ptr = TopTrailingObj::getTrailingObjectsImpl(
|
||||
Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) +
|
||||
TopTrailingObj::callNumTrailingObjects(
|
||||
Obj, TrailingObjectsBase::OverloadToken<PrevTy>());
|
||||
|
||||
if (requiresRealignment())
|
||||
return reinterpret_cast<NextTy *>(llvm::alignAddr(Ptr, alignof(NextTy)));
|
||||
else
|
||||
return reinterpret_cast<NextTy *>(Ptr);
|
||||
}
|
||||
|
||||
// Helper function for TrailingObjects::additionalSizeToAlloc: this
|
||||
// function recurses to superclasses, each of which requires one
|
||||
// fewer size_t argument, and adds its own size.
|
||||
static constexpr size_t additionalSizeToAllocImpl(
|
||||
size_t SizeSoFar, size_t Count1,
|
||||
typename ExtractSecondType<MoreTys, size_t>::type... MoreCounts) {
|
||||
return ParentType::additionalSizeToAllocImpl(
|
||||
(requiresRealignment() ? llvm::alignTo<alignof(NextTy)>(SizeSoFar)
|
||||
: SizeSoFar) +
|
||||
sizeof(NextTy) * Count1,
|
||||
MoreCounts...);
|
||||
}
|
||||
};
|
||||
|
||||
// The base case of the TrailingObjectsImpl inheritance recursion,
|
||||
// when there's no more trailing types.
|
||||
template <int Align, typename BaseTy, typename TopTrailingObj, typename PrevTy>
|
||||
class TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, PrevTy>
|
||||
: public TrailingObjectsAligner<Align> {
|
||||
protected:
|
||||
// This is a dummy method, only here so the "using" doesn't fail --
|
||||
// it will never be called, because this function recurses backwards
|
||||
// up the inheritance chain to subclasses.
|
||||
static void getTrailingObjectsImpl();
|
||||
|
||||
static constexpr size_t additionalSizeToAllocImpl(size_t SizeSoFar) {
|
||||
return SizeSoFar;
|
||||
}
|
||||
|
||||
template <bool CheckAlignment> static void verifyTrailingObjectsAlignment() {}
|
||||
};
|
||||
|
||||
} // end namespace trailing_objects_internal
|
||||
|
||||
// Finally, the main type defined in this file, the one intended for users...
|
||||
|
||||
/// See the file comment for details on the usage of the
|
||||
/// TrailingObjects type.
|
||||
template <typename BaseTy, typename... TrailingTys>
|
||||
class TrailingObjects : private trailing_objects_internal::TrailingObjectsImpl<
|
||||
trailing_objects_internal::AlignmentCalcHelper<
|
||||
TrailingTys...>::Alignment,
|
||||
BaseTy, TrailingObjects<BaseTy, TrailingTys...>,
|
||||
BaseTy, TrailingTys...> {
|
||||
|
||||
template <int A, typename B, typename T, typename P, typename... M>
|
||||
friend class trailing_objects_internal::TrailingObjectsImpl;
|
||||
|
||||
template <typename... Tys> class Foo {};
|
||||
|
||||
typedef trailing_objects_internal::TrailingObjectsImpl<
|
||||
trailing_objects_internal::AlignmentCalcHelper<TrailingTys...>::Alignment,
|
||||
BaseTy, TrailingObjects<BaseTy, TrailingTys...>, BaseTy, TrailingTys...>
|
||||
ParentType;
|
||||
using TrailingObjectsBase = trailing_objects_internal::TrailingObjectsBase;
|
||||
|
||||
using ParentType::getTrailingObjectsImpl;
|
||||
|
||||
// These two methods are the base of the recursion for this method.
|
||||
static const BaseTy *
|
||||
getTrailingObjectsImpl(const BaseTy *Obj,
|
||||
TrailingObjectsBase::OverloadToken<BaseTy>) {
|
||||
return Obj;
|
||||
}
|
||||
|
||||
static BaseTy *
|
||||
getTrailingObjectsImpl(BaseTy *Obj,
|
||||
TrailingObjectsBase::OverloadToken<BaseTy>) {
|
||||
return Obj;
|
||||
}
|
||||
|
||||
// callNumTrailingObjects simply calls numTrailingObjects on the
|
||||
// provided Obj -- except when the type being queried is BaseTy
|
||||
// itself. There is always only one of the base object, so that case
|
||||
// is handled here. (An additional benefit of indirecting through
|
||||
// this function is that consumers only say "friend
|
||||
// TrailingObjects", and thus, only this class itself can call the
|
||||
// numTrailingObjects function.)
|
||||
static size_t
|
||||
callNumTrailingObjects(const BaseTy *Obj,
|
||||
TrailingObjectsBase::OverloadToken<BaseTy>) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static size_t callNumTrailingObjects(const BaseTy *Obj,
|
||||
TrailingObjectsBase::OverloadToken<T>) {
|
||||
return Obj->numTrailingObjects(TrailingObjectsBase::OverloadToken<T>());
|
||||
}
|
||||
|
||||
public:
|
||||
// Make this (privately inherited) member public.
|
||||
#ifndef _MSC_VER
|
||||
using ParentType::OverloadToken;
|
||||
#else
|
||||
// MSVC bug prevents the above from working, at least up through CL
|
||||
// 19.10.24629.
|
||||
template <typename T>
|
||||
using OverloadToken = typename ParentType::template OverloadToken<T>;
|
||||
#endif
|
||||
|
||||
/// Returns a pointer to the trailing object array of the given type
|
||||
/// (which must be one of those specified in the class template). The
|
||||
/// array may have zero or more elements in it.
|
||||
template <typename T> const T *getTrailingObjects() const {
|
||||
// Forwards to an impl function with overloads, since member
|
||||
// function templates can't be specialized.
|
||||
return this->getTrailingObjectsImpl(
|
||||
static_cast<const BaseTy *>(this),
|
||||
TrailingObjectsBase::OverloadToken<T>());
|
||||
}
|
||||
|
||||
/// Returns a pointer to the trailing object array of the given type
|
||||
/// (which must be one of those specified in the class template). The
|
||||
/// array may have zero or more elements in it.
|
||||
template <typename T> T *getTrailingObjects() {
|
||||
// Forwards to an impl function with overloads, since member
|
||||
// function templates can't be specialized.
|
||||
return this->getTrailingObjectsImpl(
|
||||
static_cast<BaseTy *>(this), TrailingObjectsBase::OverloadToken<T>());
|
||||
}
|
||||
|
||||
/// Returns the size of the trailing data, if an object were
|
||||
/// allocated with the given counts (The counts are in the same order
|
||||
/// as the template arguments). This does not include the size of the
|
||||
/// base object. The template arguments must be the same as those
|
||||
/// used in the class; they are supplied here redundantly only so
|
||||
/// that it's clear what the counts are counting in callers.
|
||||
template <typename... Tys>
|
||||
static constexpr typename std::enable_if<
|
||||
std::is_same<Foo<TrailingTys...>, Foo<Tys...>>::value, size_t>::type
|
||||
additionalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType<
|
||||
TrailingTys, size_t>::type... Counts) {
|
||||
return ParentType::additionalSizeToAllocImpl(0, Counts...);
|
||||
}
|
||||
|
||||
/// Returns the total size of an object if it were allocated with the
|
||||
/// given trailing object counts. This is the same as
|
||||
/// additionalSizeToAlloc, except it *does* include the size of the base
|
||||
/// object.
|
||||
template <typename... Tys>
|
||||
static constexpr typename std::enable_if<
|
||||
std::is_same<Foo<TrailingTys...>, Foo<Tys...>>::value, size_t>::type
|
||||
totalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType<
|
||||
TrailingTys, size_t>::type... Counts) {
|
||||
return sizeof(BaseTy) + ParentType::additionalSizeToAllocImpl(0, Counts...);
|
||||
}
|
||||
|
||||
/// A type where its ::with_counts template member has a ::type member
|
||||
/// suitable for use as uninitialized storage for an object with the given
|
||||
/// trailing object counts. The template arguments are similar to those
|
||||
/// of additionalSizeToAlloc.
|
||||
///
|
||||
/// Use with FixedSizeStorageOwner, e.g.:
|
||||
///
|
||||
/// \code{.cpp}
|
||||
///
|
||||
/// MyObj::FixedSizeStorage<void *>::with_counts<1u>::type myStackObjStorage;
|
||||
/// MyObj::FixedSizeStorageOwner
|
||||
/// myStackObjOwner(new ((void *)&myStackObjStorage) MyObj);
|
||||
/// MyObj *const myStackObjPtr = myStackObjOwner.get();
|
||||
///
|
||||
/// \endcode
|
||||
template <typename... Tys> struct FixedSizeStorage {
|
||||
template <size_t... Counts> struct with_counts {
|
||||
enum { Size = totalSizeToAlloc<Tys...>(Counts...) };
|
||||
using type = std::aligned_storage<Size, alignof(BaseTy)>;
|
||||
};
|
||||
};
|
||||
|
||||
/// A type that acts as the owner for an object placed into fixed storage.
|
||||
class FixedSizeStorageOwner {
|
||||
public:
|
||||
FixedSizeStorageOwner(BaseTy *p) : p(p) {}
|
||||
~FixedSizeStorageOwner() {
|
||||
assert(p && "FixedSizeStorageOwner owns null?");
|
||||
p->~BaseTy();
|
||||
}
|
||||
|
||||
BaseTy *get() { return p; }
|
||||
const BaseTy *get() const { return p; }
|
||||
|
||||
private:
|
||||
FixedSizeStorageOwner(const FixedSizeStorageOwner &) = delete;
|
||||
FixedSizeStorageOwner(FixedSizeStorageOwner &&) = delete;
|
||||
FixedSizeStorageOwner &operator=(const FixedSizeStorageOwner &) = delete;
|
||||
FixedSizeStorageOwner &operator=(FixedSizeStorageOwner &&) = delete;
|
||||
|
||||
BaseTy *const p;
|
||||
};
|
||||
};
|
||||
|
||||
} // end namespace ABI
|
||||
} // end namespace swift
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif
|
||||
@@ -1,314 +0,0 @@
|
||||
//===--- ValueWitness.def - Value witness x-macros --------------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// X-macro definition file for value witness tables.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// This file is "parameterized" in the sense that exactly one of the
|
||||
// following macros *must* be defined:
|
||||
|
||||
/// WANT_ALL_VALUE_WITNESSES
|
||||
/// Define this to expand all value witnesses, not just the ones from
|
||||
/// a specific category.
|
||||
#if defined(WANT_ALL_VALUE_WITNESSES)
|
||||
#undef WANT_ALL_VALUE_WITNESSES
|
||||
#define WANT_REQUIRED_VALUE_WITNESSES 1
|
||||
#define WANT_ENUM_VALUE_WITNESSES 1
|
||||
|
||||
/// WANT_ONLY_REQUIRED_VALUE_WITNESSES
|
||||
/// Define this to expand only the required value witnesses.
|
||||
#elif defined(WANT_ONLY_REQUIRED_VALUE_WITNESSES)
|
||||
#undef WANT_ONLY_REQUIRED_VALUE_WITNESSES
|
||||
#define WANT_REQUIRED_VALUE_WITNESSES 1
|
||||
#define WANT_ENUM_VALUE_WITNESSES 0
|
||||
|
||||
/// WANT_ONLY_ENUM_VALUE_WITNESSES
|
||||
/// Define this to expand only the enum value witnesses.
|
||||
#elif defined(WANT_ONLY_ENUM_VALUE_WITNESSES)
|
||||
#undef WANT_ONLY_ENUM_VALUE_WITNESSES
|
||||
#define WANT_REQUIRED_VALUE_WITNESSES 0
|
||||
#define WANT_ENUM_VALUE_WITNESSES 1
|
||||
|
||||
/// WANT_REQUIRED_VALUE_WITNESSES
|
||||
/// WANT_ENUM_VALUE_WITNESSES
|
||||
/// Define all of these to control exactly what to expand.
|
||||
#else
|
||||
#if !defined(WANT_REQUIRED_VALUE_WITNESSES) || !defined(WANT_ENUM_VALUE_WITNESSES)
|
||||
#error failed to define a WANT macro; possible typo?
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// VALUE_WITNESS(lowerId, upperId)
|
||||
/// A fallback called for value witnesses if either of DATA_VALUE_WITNESS or
|
||||
/// FUNCTION_VALUE_WITNESS is not defined.
|
||||
|
||||
/// FUNCTION_VALUE_WITNESS(lowerId, upperId, returnType, paramTypeList)
|
||||
/// A function value witness. Types will be defined in terms of the
|
||||
/// following macros:
|
||||
/// MUTABLE_VALUE_TYPE - a pointer to a mutable opaque value
|
||||
/// IMMUTABLE_VALUE_TYPE - a pointer to an immutable opaque value
|
||||
/// MUTABLE_BUFFER_TYPE - a pointer to a fixed-size value buffer
|
||||
/// IMMUTABLE_BUFFER_TYPE - a pointer to an immutable fixed-size buffer
|
||||
/// TYPE_TYPE - a pointer to type metadata
|
||||
/// SIZE_TYPE - StoredSize
|
||||
/// INT_TYPE - int
|
||||
/// UINT_TYPE - unsigned int
|
||||
/// VOID_TYPE - void
|
||||
/// Defaults to VALUE_WITNESS.
|
||||
/// FIXME: The 'copy' witnesses should be using immutable types but aren't.
|
||||
#ifndef FUNCTION_VALUE_WITNESS
|
||||
#define FUNCTION_VALUE_WITNESS(lowerId, upperId, returnType, paramTypes) \
|
||||
VALUE_WITNESS(lowerId, upperId)
|
||||
#endif
|
||||
|
||||
/// DATA_VALUE_WITNESS(lowerId, upperId, type)
|
||||
/// A non-function value witness. Types are specified as for
|
||||
/// FUNCTION_VALUE_WITNESS
|
||||
/// Defaults to VALUE_WITNESS.
|
||||
#ifndef DATA_VALUE_WITNESS
|
||||
#define DATA_VALUE_WITNESS(lowerId, upperId, type) \
|
||||
VALUE_WITNESS(lowerId, upperId)
|
||||
#endif
|
||||
|
||||
/// Begin a range of value witnesses. This will be expanded immediately
|
||||
/// after the first value in the range, whose ID will be upperId.
|
||||
/// Range expansions do not interact well with the use of WANT_ONLY_*.
|
||||
#ifndef BEGIN_VALUE_WITNESS_RANGE
|
||||
#define BEGIN_VALUE_WITNESS_RANGE(rangeId, upperId)
|
||||
#endif
|
||||
|
||||
/// End a range of value witnesses. This will be expanded immediately
|
||||
/// after the last value in the range, whose ID will be upperId.
|
||||
/// Range expansions do not interact well with the use of WANT_ONLY_*.
|
||||
#ifndef END_VALUE_WITNESS_RANGE
|
||||
#define END_VALUE_WITNESS_RANGE(rangeId, upperId)
|
||||
#endif
|
||||
|
||||
#if WANT_REQUIRED_VALUE_WITNESSES
|
||||
|
||||
/// T *(*initializeBufferWithCopyOfBuffer)(B *dest, B *src, M *self);
|
||||
/// Given an invalid buffer, initialize it as a copy of the
|
||||
/// object in the source buffer.
|
||||
FUNCTION_VALUE_WITNESS(initializeBufferWithCopyOfBuffer,
|
||||
InitializeBufferWithCopyOfBuffer,
|
||||
MUTABLE_VALUE_TYPE,
|
||||
(MUTABLE_BUFFER_TYPE, MUTABLE_BUFFER_TYPE, TYPE_TYPE))
|
||||
|
||||
BEGIN_VALUE_WITNESS_RANGE(ValueWitness,
|
||||
InitializeBufferWithCopyOfBuffer)
|
||||
BEGIN_VALUE_WITNESS_RANGE(RequiredValueWitness,
|
||||
InitializeBufferWithCopyOfBuffer)
|
||||
BEGIN_VALUE_WITNESS_RANGE(RequiredValueWitnessFunction,
|
||||
InitializeBufferWithCopyOfBuffer)
|
||||
|
||||
/// void (*destroy)(T *object, witness_t *self);
|
||||
///
|
||||
/// Given a valid object of this type, destroy it, leaving it as an
|
||||
/// invalid object. This is useful when generically destroying
|
||||
/// an object which has been allocated in-line, such as an array,
|
||||
/// struct, or tuple element.
|
||||
FUNCTION_VALUE_WITNESS(destroy,
|
||||
Destroy,
|
||||
VOID_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, TYPE_TYPE))
|
||||
|
||||
/// T *(*initializeWithCopy)(T *dest, T *src, M *self);
|
||||
///
|
||||
/// Given an invalid object of this type, initialize it as a copy of
|
||||
/// the source object. Returns the dest object.
|
||||
FUNCTION_VALUE_WITNESS(initializeWithCopy,
|
||||
InitializeWithCopy,
|
||||
MUTABLE_VALUE_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE))
|
||||
|
||||
/// T *(*assignWithCopy)(T *dest, T *src, M *self);
|
||||
///
|
||||
/// Given a valid object of this type, change it to be a copy of the
|
||||
/// source object. Returns the dest object.
|
||||
FUNCTION_VALUE_WITNESS(assignWithCopy,
|
||||
AssignWithCopy,
|
||||
MUTABLE_VALUE_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE))
|
||||
|
||||
/// T *(*initializeWithTake)(T *dest, T *src, M *self);
|
||||
///
|
||||
/// Given an invalid object of this type, initialize it by taking
|
||||
/// the value of the source object. The source object becomes
|
||||
/// invalid. Returns the dest object.
|
||||
FUNCTION_VALUE_WITNESS(initializeWithTake,
|
||||
InitializeWithTake,
|
||||
MUTABLE_VALUE_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE))
|
||||
|
||||
/// T *(*assignWithTake)(T *dest, T *src, M *self);
|
||||
///
|
||||
/// Given a valid object of this type, change it to be a copy of the
|
||||
/// source object. The source object becomes invalid. Returns the
|
||||
/// dest object.
|
||||
FUNCTION_VALUE_WITNESS(assignWithTake,
|
||||
AssignWithTake,
|
||||
MUTABLE_VALUE_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE))
|
||||
|
||||
/// unsigned (*getEnumTagSinglePayload)(const T* enum, UINT_TYPE emptyCases,
|
||||
/// M *self);
|
||||
/// Given an instance of valid single payload enum with a payload of this
|
||||
/// witness table's type (e.g Optional<ThisType>) , get the tag of the enum.
|
||||
FUNCTION_VALUE_WITNESS(getEnumTagSinglePayload,
|
||||
GetEnumTagSinglePayload,
|
||||
UINT_TYPE,
|
||||
(IMMUTABLE_VALUE_TYPE, UINT_TYPE, TYPE_TYPE))
|
||||
|
||||
/// void (*storeEnumTagSinglePayload)(T* enum, UINT_TYPE whichCase,
|
||||
/// UINT_TYPE emptyCases, M *self);
|
||||
/// Given uninitialized memory for an instance of a single payload enum with a
|
||||
/// payload of this witness table's type (e.g Optional<ThisType>), store the
|
||||
/// tag.
|
||||
FUNCTION_VALUE_WITNESS(storeEnumTagSinglePayload,
|
||||
StoreEnumTagSinglePayload,
|
||||
VOID_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, UINT_TYPE, UINT_TYPE, TYPE_TYPE))
|
||||
|
||||
END_VALUE_WITNESS_RANGE(RequiredValueWitnessFunction,
|
||||
StoreEnumTagSinglePayload)
|
||||
|
||||
/// SIZE_TYPE size;
|
||||
///
|
||||
/// The required storage size of a single object of this type.
|
||||
DATA_VALUE_WITNESS(size,
|
||||
Size,
|
||||
SIZE_TYPE)
|
||||
|
||||
BEGIN_VALUE_WITNESS_RANGE(TypeLayoutWitness,
|
||||
Size)
|
||||
|
||||
BEGIN_VALUE_WITNESS_RANGE(RequiredTypeLayoutWitness,
|
||||
Size)
|
||||
|
||||
/// SIZE_TYPE stride;
|
||||
///
|
||||
/// The required size per element of an array of this type. It is at least
|
||||
/// one, even for zero-sized types, like the empty tuple.
|
||||
DATA_VALUE_WITNESS(stride,
|
||||
Stride,
|
||||
SIZE_TYPE)
|
||||
|
||||
|
||||
/// UINT_TYPE flags;
|
||||
///
|
||||
/// The ValueWitnessAlignmentMask bits represent the required
|
||||
/// alignment of the first byte of an object of this type, expressed
|
||||
/// as a mask of the low bits that must not be set in the pointer.
|
||||
/// This representation can be easily converted to the 'alignof'
|
||||
/// result by merely adding 1, but it is more directly useful for
|
||||
/// performing dynamic structure layouts, and it grants an
|
||||
/// additional bit of precision in a compact field without needing
|
||||
/// to switch to an exponent representation.
|
||||
///
|
||||
/// The ValueWitnessIsNonPOD bit is set if the type is not POD.
|
||||
///
|
||||
/// The ValueWitnessIsNonInline bit is set if the type cannot be
|
||||
/// represented in a fixed-size buffer or if it is not bitwise takable.
|
||||
///
|
||||
/// The ExtraInhabitantsMask bits represent the number of "extra inhabitants"
|
||||
/// of the bit representation of the value that do not form valid values of
|
||||
/// the type.
|
||||
///
|
||||
/// The Enum_HasSpareBits bit is set if the type's binary representation
|
||||
/// has unused bits.
|
||||
///
|
||||
/// The HasEnumWitnesses bit is set if the type is an enum type.
|
||||
DATA_VALUE_WITNESS(flags,
|
||||
Flags,
|
||||
UINT_TYPE)
|
||||
|
||||
/// UINT_TYPE extraInhabitantCount;
|
||||
///
|
||||
/// The number of extra inhabitants in the type.
|
||||
DATA_VALUE_WITNESS(extraInhabitantCount,
|
||||
ExtraInhabitantCount,
|
||||
UINT_TYPE)
|
||||
|
||||
END_VALUE_WITNESS_RANGE(RequiredTypeLayoutWitness,
|
||||
ExtraInhabitantCount)
|
||||
|
||||
END_VALUE_WITNESS_RANGE(RequiredValueWitness,
|
||||
ExtraInhabitantCount)
|
||||
|
||||
END_VALUE_WITNESS_RANGE(TypeLayoutWitness,
|
||||
ExtraInhabitantCount)
|
||||
|
||||
#endif /* WANT_REQUIRED_VALUE_WITNESSES */
|
||||
|
||||
#if WANT_ENUM_VALUE_WITNESSES
|
||||
|
||||
// The following value witnesses are conditionally present if the witnessed
|
||||
// type is an enum.
|
||||
|
||||
/// unsigned (*getEnumTag)(T *obj, M *self);
|
||||
///
|
||||
/// Given a valid object of this enum type, extracts the tag value indicating
|
||||
/// which case of the enum is inhabited. Returned values are in the range
|
||||
/// [0..NumElements-1].
|
||||
FUNCTION_VALUE_WITNESS(getEnumTag,
|
||||
GetEnumTag,
|
||||
INT_TYPE,
|
||||
(IMMUTABLE_VALUE_TYPE, TYPE_TYPE))
|
||||
|
||||
BEGIN_VALUE_WITNESS_RANGE(EnumValueWitness,
|
||||
GetEnumTag)
|
||||
|
||||
/// void (*destructiveProjectEnumData)(T *obj, M *self);
|
||||
/// Given a valid object of this enum type, destructively extracts the
|
||||
/// associated payload.
|
||||
FUNCTION_VALUE_WITNESS(destructiveProjectEnumData,
|
||||
DestructiveProjectEnumData,
|
||||
VOID_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, TYPE_TYPE))
|
||||
|
||||
/// void (*destructiveInjectEnumTag)(T *obj, unsigned tag, M *self);
|
||||
/// Given an enum case tag and a valid object of case's payload type,
|
||||
/// destructively inserts the tag into the payload. The given tag value
|
||||
/// must be in the range [-ElementsWithPayload..ElementsWithNoPayload-1].
|
||||
FUNCTION_VALUE_WITNESS(destructiveInjectEnumTag,
|
||||
DestructiveInjectEnumTag,
|
||||
VOID_TYPE,
|
||||
(MUTABLE_VALUE_TYPE, UINT_TYPE, TYPE_TYPE))
|
||||
|
||||
END_VALUE_WITNESS_RANGE(EnumValueWitness,
|
||||
DestructiveInjectEnumTag)
|
||||
|
||||
END_VALUE_WITNESS_RANGE(ValueWitness,
|
||||
DestructiveInjectEnumTag)
|
||||
|
||||
#endif /* WANT_ENUM_VALUE_WITNESSES */
|
||||
|
||||
#undef MUTABLE_VALUE_TYPE
|
||||
#undef IMMUTABLE_VALUE_TYPE
|
||||
#undef MUTABLE_BUFFER_TYPE
|
||||
#undef IMMUTABLE_BUFFER_TYPE
|
||||
#undef TYPE_TYPE
|
||||
#undef SIZE_TYPE
|
||||
#undef INT_TYPE
|
||||
#undef VOID_TYPE
|
||||
|
||||
#undef END_VALUE_WITNESS_RANGE
|
||||
#undef BEGIN_VALUE_WITNESS_RANGE
|
||||
#undef DATA_VALUE_WITNESS
|
||||
#undef FUNCTION_VALUE_WITNESS
|
||||
#undef VALUE_WITNESS
|
||||
#undef ENUM_VALUE_WITNESS
|
||||
#undef NON_REQUIRED_VALUE_WITNESS
|
||||
#undef REQUIRED_VALUE_WITNESS
|
||||
#undef WANT_ENUM_VALUE_WITNESSES
|
||||
#undef WANT_REQUIRED_VALUE_WITNESSES
|
||||
@@ -1,129 +0,0 @@
|
||||
//===--- FlagSet.h - Helper class for opaque flag types ---------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the FlagSet template, a class which makes it easier to
|
||||
// define opaque flag types.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The swift namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_SWIFT_BASIC_FLAGSET_H
|
||||
#define OPENCOMBINE_SWIFT_BASIC_FLAGSET_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <assert.h>
|
||||
|
||||
namespace opencombine {
|
||||
namespace swift {
|
||||
|
||||
/// A template designed to simplify the task of defining a wrapper type
|
||||
/// for a flags bitfield.
|
||||
///
|
||||
/// Unfortunately, this doesn't currently support functional-style
|
||||
/// building patterns, which means this can't practically be used for
|
||||
/// types that need to be used in constant expressions.
|
||||
template <typename IntType>
|
||||
class FlagSet {
|
||||
static_assert(std::is_integral<IntType>::value,
|
||||
"storage type for FlagSet must be an integral type");
|
||||
IntType Bits;
|
||||
|
||||
protected:
|
||||
template <unsigned BitWidth>
|
||||
static constexpr IntType lowMaskFor() {
|
||||
return IntType((1 << BitWidth) - 1);
|
||||
}
|
||||
|
||||
template <unsigned FirstBit, unsigned BitWidth = 1>
|
||||
static constexpr IntType maskFor() {
|
||||
return lowMaskFor<BitWidth>() << FirstBit;
|
||||
}
|
||||
|
||||
constexpr FlagSet(IntType bits = 0) : Bits(bits) {}
|
||||
|
||||
/// Read a single-bit flag.
|
||||
template <unsigned Bit>
|
||||
bool getFlag() const {
|
||||
return Bits & maskFor<Bit>();
|
||||
}
|
||||
|
||||
/// Set a single-bit flag.
|
||||
template <unsigned Bit>
|
||||
void setFlag(bool value) {
|
||||
if (value) {
|
||||
Bits |= maskFor<Bit>();
|
||||
} else {
|
||||
Bits &= ~maskFor<Bit>();
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a multi-bit field.
|
||||
template <unsigned FirstBit, unsigned BitWidth, typename FieldType = IntType>
|
||||
FieldType getField() const {
|
||||
return FieldType((Bits >> FirstBit) & lowMaskFor<BitWidth>());
|
||||
}
|
||||
|
||||
/// Assign to a multi-bit field.
|
||||
template <unsigned FirstBit, unsigned BitWidth, typename FieldType = IntType>
|
||||
void setField(typename std::enable_if<true, FieldType>::type value) {
|
||||
// Note that we suppress template argument deduction for FieldType.
|
||||
assert(IntType(value) <= lowMaskFor<BitWidth>() && "value out of range");
|
||||
Bits = (Bits & ~maskFor<FirstBit, BitWidth>())
|
||||
| (IntType(value) << FirstBit);
|
||||
}
|
||||
|
||||
// A convenient macro for defining a getter and setter for a flag.
|
||||
// Intended to be used in the body of a subclass of FlagSet.
|
||||
#define FLAGSET_DEFINE_FLAG_ACCESSORS(BIT, GETTER, SETTER) \
|
||||
bool GETTER() const { \
|
||||
return this->template getFlag<BIT>(); \
|
||||
} \
|
||||
void SETTER(bool value) { \
|
||||
this->template setFlag<BIT>(value); \
|
||||
}
|
||||
|
||||
// A convenient macro for defining a getter and setter for a field.
|
||||
// Intended to be used in the body of a subclass of FlagSet.
|
||||
#define FLAGSET_DEFINE_FIELD_ACCESSORS(BIT, WIDTH, TYPE, GETTER, SETTER) \
|
||||
TYPE GETTER() const { \
|
||||
return this->template getField<BIT, WIDTH, TYPE>(); \
|
||||
} \
|
||||
void SETTER(TYPE value) { \
|
||||
this->template setField<BIT, WIDTH, TYPE>(value); \
|
||||
}
|
||||
|
||||
// A convenient macro to expose equality operators.
|
||||
// These can't be provided directly by FlagSet because that would allow
|
||||
// different flag sets to be compared if they happen to have the same
|
||||
// underlying type.
|
||||
#define FLAGSET_DEFINE_EQUALITY(TYPENAME) \
|
||||
friend bool operator==(TYPENAME lhs, TYPENAME rhs) { \
|
||||
return lhs.getOpaqueValue() == rhs.getOpaqueValue(); \
|
||||
} \
|
||||
friend bool operator!=(TYPENAME lhs, TYPENAME rhs) { \
|
||||
return lhs.getOpaqueValue() != rhs.getOpaqueValue(); \
|
||||
}
|
||||
|
||||
public:
|
||||
/// Get the bits as an opaque integer value.
|
||||
IntType getOpaqueValue() const {
|
||||
return Bits;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace swift
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif
|
||||
@@ -1,568 +0,0 @@
|
||||
//===--- RelativePointer.h - Relative Pointer Support -----------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
///
|
||||
/// Some data structures emitted by the Swift compiler use relative indirect
|
||||
/// addresses in order to minimize startup cost for a process. By referring to
|
||||
/// the offset of the global offset table entry for a symbol, instead of
|
||||
/// directly referring to the symbol, compiler-emitted data structures avoid
|
||||
/// requiring unnecessary relocation at dynamic linking time. This header
|
||||
/// contains types to help dereference these relative addresses.
|
||||
///
|
||||
/// Theory of references to objects
|
||||
/// -------------------------------
|
||||
///
|
||||
/// A reference can be absolute or relative:
|
||||
///
|
||||
/// - An absolute reference is a pointer to the object.
|
||||
///
|
||||
/// - A relative reference is a (signed) offset from the address of the
|
||||
/// reference to the address of its direct referent.
|
||||
///
|
||||
/// A relative reference can be direct, indirect, or symbolic.
|
||||
///
|
||||
/// In a direct reference, the direct referent is simply the target object.
|
||||
/// Generally, a statically-emitted relative reference can only be direct
|
||||
/// if it can be resolved to a constant offset by the linker, because loaders
|
||||
/// do not support forming relative references. This means that either the
|
||||
/// reference and object must lie within the same linkage unit or the
|
||||
/// difference must be computed at runtime by code.
|
||||
///
|
||||
/// In a symbolic reference, the direct referent is a string holding the symbol
|
||||
/// name of the object. A relative reference can only be symbolic if the
|
||||
/// object actually has a symbol at runtime, which may require exporting
|
||||
/// many internal symbols that would otherwise be strippable.
|
||||
///
|
||||
/// In an indirect reference, the direct referent is a variable holding an
|
||||
/// absolute reference to the object. An indirect relative reference may
|
||||
/// refer to an arbitrary symbol, be it anonymous within the linkage unit
|
||||
/// or completely external to it, but it requires the introduction of an
|
||||
/// intermediate absolute reference that requires load-time initialization.
|
||||
/// However, this initialization can be shared among all indirect references
|
||||
/// within the linkage unit, and the linker will generally place all such
|
||||
/// references adjacent to one another to improve load-time locality.
|
||||
///
|
||||
/// A reference can be made a dynamic union of more than one of these options.
|
||||
/// This allows the compiler/linker to use a direct reference when possible
|
||||
/// and a less-efficient option where required. However, it also requires
|
||||
/// the cases to be dynamically distinguished. This can be done by setting
|
||||
/// a low bit of the offset, as long as the difference between the direct
|
||||
/// referent's address and the reference is a multiple of 2. This works well
|
||||
/// for "indirectable" references because most objects are known to be
|
||||
/// well-aligned, and the cases that aren't (chiefly functions and strings)
|
||||
/// rarely need the flexibility of this kind of reference. It does not
|
||||
/// work quite as well for "possibly symbolic" references because C strings
|
||||
/// are not naturally aligned, and making them aligned generally requires
|
||||
/// moving them out of the linker's ordinary string section; however, it's
|
||||
/// still workable.
|
||||
///
|
||||
/// Finally, a relative reference can be near or far. A near reference
|
||||
/// is potentially smaller, but it requires the direct referent to lie
|
||||
/// within a certain distance of the reference, even if dynamically
|
||||
/// initialized.
|
||||
///
|
||||
/// In Swift, we always prefer to use a near direct relative reference
|
||||
/// when it is possible to do so: that is, when the relationship is always
|
||||
/// between two global objects emitted in the same linkage unit, and there
|
||||
/// is no compatibility constraint requiring the use of an absolute reference.
|
||||
///
|
||||
/// When more flexibility is required, there are several options:
|
||||
///
|
||||
/// 1. Use an absolute reference. Size penalty on 64-bit. Requires
|
||||
/// load-time work.
|
||||
///
|
||||
/// 2. Use a far direct relative reference. Size penalty on 64-bit.
|
||||
/// Requires load-time work when object is outside linkage unit.
|
||||
/// Generally not directly supported by loaders.
|
||||
///
|
||||
/// 3. Use an always-indirect relative reference. Size penalty of one
|
||||
/// pointer (shared). Requires load-time work even when object is
|
||||
/// within linkage unit.
|
||||
///
|
||||
/// 4. Use a near indirectable relative reference. Size penalty of one
|
||||
/// pointer (shared) when reference exceeds range. Runtime / code-size
|
||||
/// penalty on access. Requires load-time work (shared) only when
|
||||
/// object is outside linkage unit.
|
||||
///
|
||||
/// 5. Use a far indirectable relative reference. Size penalty on 64-bit.
|
||||
/// Size penalty of one pointer (shared) when reference exceeds range
|
||||
/// and is initialized statically. Runtime / code-size penalty on access.
|
||||
/// Requires load-time work (shared) only when object is outside linkage
|
||||
/// unit.
|
||||
///
|
||||
/// 6. Use a near or far symbolic relative reference. No load-time work.
|
||||
/// Severe runtime penalty on access. Requires custom logic to statically
|
||||
/// optimize. Requires emission of symbol for target even if private
|
||||
/// to linkage unit.
|
||||
///
|
||||
/// 7. Use a near or far direct-or-symbolic relative reference. No
|
||||
/// load-time work. Severe runtime penalty on access if object is
|
||||
/// outside of linkage unit. Requires custom logic to statically optimize.
|
||||
///
|
||||
/// In general, it's our preference in Swift to use option #4 when there
|
||||
/// is no possibility of initializing the reference dynamically and option #5
|
||||
/// when there is. This is because it is infeasible to actually share the
|
||||
/// memory for the intermediate absolute reference when it must be allocated
|
||||
/// dynamically.
|
||||
///
|
||||
/// Symbolic references are an interesting idea that we have not yet made
|
||||
/// use of. They may be acceptable in reflective metadata cases where it
|
||||
/// is desirable to heavily bias towards never using the metadata. However,
|
||||
/// they're only profitable if there wasn't any other indirect reference
|
||||
/// to the target, and it is likely that their optimal use requires a more
|
||||
/// intelligent toolchain from top to bottom.
|
||||
///
|
||||
/// Note that the cost of load-time work also includes a binary-size penalty
|
||||
/// to store the loader metadata necessary to perform that work. Therefore
|
||||
/// it is better to avoid it even when there are dynamic optimizations in
|
||||
/// place to skip the work itself.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - The swift namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_SWIFT_BASIC_RELATIVEPOINTER_H
|
||||
#define OPENCOMBINE_SWIFT_BASIC_RELATIVEPOINTER_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace opencombine {
|
||||
namespace swift {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// Apply a relative offset to a base pointer. The offset is applied to the base
|
||||
/// pointer using sign-extended, wrapping arithmetic.
|
||||
template<typename BasePtrTy, typename Offset>
|
||||
static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
auto base = reinterpret_cast<uintptr_t>(basePtr);
|
||||
// We want to do wrapping arithmetic, but with a sign-extended
|
||||
// offset. To do this in C, we need to do signed promotion to get
|
||||
// the sign extension, but we need to perform arithmetic on unsigned values,
|
||||
// since signed overflow is undefined behavior.
|
||||
auto extendOffset = (uintptr_t)(intptr_t)offset;
|
||||
return base + extendOffset;
|
||||
}
|
||||
|
||||
/// Measure the relative offset between two pointers. This measures
|
||||
/// (referent - base) using wrapping arithmetic. The result is truncated if
|
||||
/// Offset is smaller than a pointer, with an assertion that the
|
||||
/// pre-truncation result is a sign extension of the truncated result.
|
||||
template<typename Offset, typename A, typename B>
|
||||
static inline Offset measureRelativeOffset(A *referent, B *base) {
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
auto distance = (uintptr_t)referent - (uintptr_t)base;
|
||||
// Truncate as unsigned, then wrap around to signed.
|
||||
auto truncatedDistance =
|
||||
(Offset)(typename std::make_unsigned<Offset>::type)distance;
|
||||
// Assert that the truncation didn't discard any non-sign-extended bits.
|
||||
assert((intptr_t)truncatedDistance == (intptr_t)distance
|
||||
&& "pointers are too far apart to fit in offset type");
|
||||
return truncatedDistance;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// A relative reference to an object stored in memory. The reference may be
|
||||
/// direct or indirect, and uses the low bit of the (assumed at least
|
||||
/// 2-byte-aligned) pointer to differentiate.
|
||||
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t>
|
||||
class RelativeIndirectPointer {
|
||||
private:
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
/// The relative offset of the pointer's memory from the `this` pointer.
|
||||
/// This is an indirect reference.
|
||||
Offset RelativeOffset;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeIndirectPointer() = delete;
|
||||
RelativeIndirectPointer(RelativeIndirectPointer &&) = delete;
|
||||
RelativeIndirectPointer(const RelativeIndirectPointer &) = delete;
|
||||
RelativeIndirectPointer &operator=(RelativeIndirectPointer &&)
|
||||
= delete;
|
||||
RelativeIndirectPointer &operator=(const RelativeIndirectPointer &)
|
||||
= delete;
|
||||
|
||||
public:
|
||||
const ValueTy *get() const & {
|
||||
// Check for null.
|
||||
if (Nullable && RelativeOffset == 0)
|
||||
return nullptr;
|
||||
|
||||
uintptr_t address = detail::applyRelativeOffset(this, RelativeOffset);
|
||||
return *reinterpret_cast<const ValueTy * const *>(address);
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
return RelativeOffset == 0;
|
||||
}
|
||||
|
||||
operator const ValueTy* () const & {
|
||||
return get();
|
||||
}
|
||||
|
||||
const ValueTy *operator->() const & {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
|
||||
/// A relative reference to an object stored in memory. The reference may be
|
||||
/// direct or indirect, and uses the low bit of the (assumed at least
|
||||
/// 2-byte-aligned) pointer to differentiate.
|
||||
template<typename ValueTy, bool Nullable = false, typename Offset = int32_t>
|
||||
class RelativeIndirectablePointer {
|
||||
private:
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
/// The relative offset of the pointer's memory from the `this` pointer.
|
||||
/// If the low bit is clear, this is a direct reference; otherwise, it is
|
||||
/// an indirect reference.
|
||||
Offset RelativeOffsetPlusIndirect;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeIndirectablePointer() = delete;
|
||||
RelativeIndirectablePointer(RelativeIndirectablePointer &&) = delete;
|
||||
RelativeIndirectablePointer(const RelativeIndirectablePointer &) = delete;
|
||||
RelativeIndirectablePointer &operator=(RelativeIndirectablePointer &&)
|
||||
= delete;
|
||||
RelativeIndirectablePointer &operator=(const RelativeIndirectablePointer &)
|
||||
= delete;
|
||||
|
||||
public:
|
||||
/// Allow construction and reassignment from an absolute pointer.
|
||||
/// These always produce a direct relative offset.
|
||||
RelativeIndirectablePointer(ValueTy *absolute)
|
||||
: RelativeOffsetPlusIndirect(
|
||||
Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this)) {
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
}
|
||||
|
||||
RelativeIndirectablePointer &operator=(ValueTy *absolute) & {
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
|
||||
RelativeOffsetPlusIndirect = Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ValueTy *get() const & {
|
||||
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
|
||||
"alignment of value and offset must be at least 2 to "
|
||||
"make room for indirectable flag");
|
||||
|
||||
// Check for null.
|
||||
if (Nullable && RelativeOffsetPlusIndirect == 0)
|
||||
return nullptr;
|
||||
|
||||
Offset offsetPlusIndirect = RelativeOffsetPlusIndirect;
|
||||
uintptr_t address = detail::applyRelativeOffset(this,
|
||||
offsetPlusIndirect & ~1);
|
||||
|
||||
// If the low bit is set, then this is an indirect address. Otherwise,
|
||||
// it's direct.
|
||||
if (offsetPlusIndirect & 1) {
|
||||
return *reinterpret_cast<const ValueTy * const *>(address);
|
||||
} else {
|
||||
return reinterpret_cast<const ValueTy *>(address);
|
||||
}
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
return RelativeOffsetPlusIndirect == 0;
|
||||
}
|
||||
|
||||
operator const ValueTy* () const & {
|
||||
return get();
|
||||
}
|
||||
|
||||
const ValueTy *operator->() const & {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
|
||||
/// A relative reference to an aligned object stored in memory. The reference
|
||||
/// may be direct or indirect, and uses the low bit of the (assumed at least
|
||||
/// 2-byte-aligned) pointer to differentiate. The remaining low bits store
|
||||
/// an additional tiny integer value.
|
||||
template<typename ValueTy, typename IntTy, bool Nullable = false,
|
||||
typename Offset = int32_t>
|
||||
class RelativeIndirectablePointerIntPair {
|
||||
private:
|
||||
static_assert(std::is_integral<Offset>::value &&
|
||||
std::is_signed<Offset>::value,
|
||||
"offset type should be signed integer");
|
||||
|
||||
/// The relative offset of the pointer's memory from the `this` pointer.
|
||||
/// If the low bit is clear, this is a direct reference; otherwise, it is
|
||||
/// an indirect reference.
|
||||
Offset RelativeOffsetPlusIndirectAndInt;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeIndirectablePointerIntPair() = delete;
|
||||
RelativeIndirectablePointerIntPair(
|
||||
RelativeIndirectablePointerIntPair &&) = delete;
|
||||
RelativeIndirectablePointerIntPair(
|
||||
const RelativeIndirectablePointerIntPair &) = delete;
|
||||
RelativeIndirectablePointerIntPair& operator=(
|
||||
RelativeIndirectablePointerIntPair &&) = delete;
|
||||
RelativeIndirectablePointerIntPair &operator=(
|
||||
const RelativeIndirectablePointerIntPair &) = delete;
|
||||
|
||||
// Retrieve the mask for the stored integer value.
|
||||
static Offset getIntMask() {
|
||||
return (alignof(Offset) - 1) & ~(Offset)0x01;
|
||||
}
|
||||
|
||||
public:
|
||||
const ValueTy *getPointer() const & {
|
||||
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
|
||||
"alignment of value and offset must be at least 2 to "
|
||||
"make room for indirectable flag");
|
||||
|
||||
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
|
||||
|
||||
// Check for null.
|
||||
if (Nullable && offset == 0)
|
||||
return nullptr;
|
||||
|
||||
Offset offsetPlusIndirect = offset;
|
||||
uintptr_t address = detail::applyRelativeOffset(this,
|
||||
offsetPlusIndirect & ~1);
|
||||
|
||||
// If the low bit is set, then this is an indirect address. Otherwise,
|
||||
// it's direct.
|
||||
if (offsetPlusIndirect & 1) {
|
||||
return *reinterpret_cast<const ValueTy * const *>(address);
|
||||
} else {
|
||||
return reinterpret_cast<const ValueTy *>(address);
|
||||
}
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
|
||||
return offset == 0;
|
||||
}
|
||||
|
||||
IntTy getInt() const & {
|
||||
return IntTy((RelativeOffsetPlusIndirectAndInt & getIntMask()) >> 1);
|
||||
}
|
||||
};
|
||||
|
||||
/// A relative reference to a function, intended to reference private metadata
|
||||
/// functions for the current executable or dynamic library image from
|
||||
/// position-independent constant data.
|
||||
template<typename T, bool Nullable, typename Offset>
|
||||
class RelativeDirectPointerImpl {
|
||||
private:
|
||||
/// The relative offset of the function's entry point from *this.
|
||||
Offset RelativeOffset;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeDirectPointerImpl() = delete;
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeDirectPointerImpl(RelativeDirectPointerImpl &&) = delete;
|
||||
RelativeDirectPointerImpl(const RelativeDirectPointerImpl &) = delete;
|
||||
RelativeDirectPointerImpl &operator=(RelativeDirectPointerImpl &&)
|
||||
= delete;
|
||||
RelativeDirectPointerImpl &operator=(const RelativeDirectPointerImpl &)
|
||||
= delete;
|
||||
|
||||
|
||||
public:
|
||||
using ValueTy = T;
|
||||
using PointerTy = T*;
|
||||
|
||||
// Allow construction and reassignment from an absolute pointer.
|
||||
RelativeDirectPointerImpl(PointerTy absolute)
|
||||
: RelativeOffset(Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this))
|
||||
{
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
}
|
||||
explicit constexpr RelativeDirectPointerImpl(std::nullptr_t)
|
||||
: RelativeOffset (0) {
|
||||
static_assert(Nullable, "can't construct non-nullable pointer from null");
|
||||
}
|
||||
|
||||
RelativeDirectPointerImpl &operator=(PointerTy absolute) & {
|
||||
if (!Nullable)
|
||||
assert(absolute != nullptr &&
|
||||
"constructing non-nullable relative pointer from null");
|
||||
RelativeOffset = Nullable && absolute == nullptr
|
||||
? 0
|
||||
: detail::measureRelativeOffset<Offset>(absolute, this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PointerTy get() const & {
|
||||
// Check for null.
|
||||
if (Nullable && RelativeOffset == 0)
|
||||
return nullptr;
|
||||
|
||||
// The value is addressed relative to `this`.
|
||||
uintptr_t absolute = detail::applyRelativeOffset(this, RelativeOffset);
|
||||
return reinterpret_cast<PointerTy>(absolute);
|
||||
}
|
||||
|
||||
/// A zero relative offset encodes a null reference.
|
||||
bool isNull() const & {
|
||||
return RelativeOffset == 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// A direct relative reference to an object.
|
||||
template<typename T, bool Nullable = true, typename Offset = int32_t>
|
||||
class RelativeDirectPointer :
|
||||
private RelativeDirectPointerImpl<T, Nullable, Offset>
|
||||
{
|
||||
using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
|
||||
public:
|
||||
using super::get;
|
||||
using super::super;
|
||||
|
||||
RelativeDirectPointer &operator=(T *absolute) & {
|
||||
super::operator=(absolute);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator typename super::PointerTy() const & {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
const typename super::ValueTy *operator->() const & {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
using super::isNull;
|
||||
};
|
||||
|
||||
/// A specialization of RelativeDirectPointer for function pointers,
|
||||
/// allowing for calls.
|
||||
template<typename RetTy, typename...ArgTy, bool Nullable, typename Offset>
|
||||
class RelativeDirectPointer<RetTy (ArgTy...), Nullable, Offset> :
|
||||
private RelativeDirectPointerImpl<RetTy (ArgTy...), Nullable, Offset>
|
||||
{
|
||||
using super = RelativeDirectPointerImpl<RetTy (ArgTy...), Nullable, Offset>;
|
||||
public:
|
||||
using super::get;
|
||||
using super::super;
|
||||
|
||||
RelativeDirectPointer &operator=(RetTy (*absolute)(ArgTy...)) & {
|
||||
super::operator=(absolute);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator typename super::PointerTy() const & {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
RetTy operator()(ArgTy...arg) const {
|
||||
return this->get()(std::forward<ArgTy>(arg)...);
|
||||
}
|
||||
|
||||
using super::isNull;
|
||||
};
|
||||
|
||||
/// A direct relative reference to an aligned object, with an additional
|
||||
/// tiny integer value crammed into its low bits.
|
||||
template<typename PointeeTy, typename IntTy, bool Nullable = false,
|
||||
typename Offset = int32_t>
|
||||
class RelativeDirectPointerIntPair {
|
||||
Offset RelativeOffsetPlusInt;
|
||||
|
||||
/// RelativePointers should appear in statically-generated metadata. They
|
||||
/// shouldn't be constructed or copied.
|
||||
RelativeDirectPointerIntPair() = delete;
|
||||
RelativeDirectPointerIntPair(RelativeDirectPointerIntPair &&) = delete;
|
||||
RelativeDirectPointerIntPair(const RelativeDirectPointerIntPair &) = delete;
|
||||
RelativeDirectPointerIntPair &operator=(RelativeDirectPointerIntPair &&)
|
||||
= delete;
|
||||
RelativeDirectPointerIntPair &operator=(const RelativeDirectPointerIntPair&)
|
||||
= delete;
|
||||
|
||||
static Offset getMask() {
|
||||
return alignof(Offset) - 1;
|
||||
}
|
||||
|
||||
public:
|
||||
using ValueTy = PointeeTy;
|
||||
using PointerTy = PointeeTy*;
|
||||
|
||||
PointerTy getPointer() const & {
|
||||
Offset offset = (RelativeOffsetPlusInt & ~getMask());
|
||||
|
||||
// Check for null.
|
||||
if (Nullable && offset == 0)
|
||||
return nullptr;
|
||||
|
||||
// The value is addressed relative to `this`.
|
||||
uintptr_t absolute = detail::applyRelativeOffset(this, offset);
|
||||
return reinterpret_cast<PointerTy>(absolute);
|
||||
}
|
||||
|
||||
IntTy getInt() const & {
|
||||
return IntTy(RelativeOffsetPlusInt & getMask());
|
||||
}
|
||||
|
||||
Offset getOpaqueValue() const & {
|
||||
return RelativeOffsetPlusInt;
|
||||
}
|
||||
};
|
||||
|
||||
// Type aliases for "far" relative pointers, which need to be able to reach
|
||||
// across the full address space instead of only across a single small-code-
|
||||
// model image.
|
||||
|
||||
template<typename T, bool Nullable = false>
|
||||
using FarRelativeIndirectablePointer =
|
||||
RelativeIndirectablePointer<T, Nullable, intptr_t>;
|
||||
|
||||
template<typename T, bool Nullable = false>
|
||||
using FarRelativeDirectPointer = RelativeDirectPointer<T, Nullable, intptr_t>;
|
||||
|
||||
} // end namespace swift
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_SWIFT_BASIC_RELATIVEPOINTER_H
|
||||
@@ -1,44 +0,0 @@
|
||||
//===--- Demangle.h - Interface to Swift symbol demangling ------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is the public API of the demangler library.
|
||||
// Tools which use the demangler library (like lldb) must include this - and
|
||||
// only this - header file.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - Some declarations have been removed.
|
||||
// - The swift namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_SWIFT_DEMANGLING_DEMANGLE_H
|
||||
#define OPENCOMBINE_SWIFT_DEMANGLING_DEMANGLE_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include "stl_polyfill/string_view.h"
|
||||
|
||||
namespace opencombine {
|
||||
namespace swift {
|
||||
namespace Demangle {
|
||||
/// Form a StringRef around the mangled name starting at base, if the name may
|
||||
/// contain symbolic references.
|
||||
string_view makeSymbolicMangledNameStringRef(const char *base);
|
||||
|
||||
} // end namespace Demangle
|
||||
} // end namespace swift
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_SWIFT_DEMANGLING_DEMANGLE_H
|
||||
@@ -1,664 +0,0 @@
|
||||
//===--- Records.h - Swift Type Reflection Records --------------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Implements the structures of type reflection records.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - Some declarations have been removed.
|
||||
// - The swift namespace is wrapped in the opencombine namespace.
|
||||
|
||||
#ifndef OPENCOMBINE_SWIFT_REFLECTION_RECORDS_H
|
||||
#define OPENCOMBINE_SWIFT_REFLECTION_RECORDS_H
|
||||
|
||||
#include "swift/Basic/RelativePointer.h"
|
||||
#include "swift/Demangling/Demangle.h"
|
||||
#include "stl_polyfill/string_view.h"
|
||||
#include "stl_polyfill/span.h"
|
||||
|
||||
namespace opencombine {
|
||||
namespace swift {
|
||||
|
||||
const uint16_t SWIFT_REFLECTION_METADATA_VERSION = 3; // superclass field
|
||||
|
||||
namespace reflection {
|
||||
|
||||
// Field records describe the type of a single stored property or case member
|
||||
// of a class, struct or enum.
|
||||
class FieldRecordFlags {
|
||||
using int_type = uint32_t;
|
||||
enum : int_type {
|
||||
// Is this an indirect enum case?
|
||||
IsIndirectCase = 0x1,
|
||||
|
||||
// Is this a mutable `var` property?
|
||||
IsVar = 0x2,
|
||||
};
|
||||
int_type Data = 0;
|
||||
|
||||
public:
|
||||
bool isIndirectCase() const {
|
||||
return (Data & IsIndirectCase) == IsIndirectCase;
|
||||
}
|
||||
|
||||
bool isVar() const {
|
||||
return (Data & IsVar) == IsVar;
|
||||
}
|
||||
|
||||
void setIsIndirectCase(bool IndirectCase=true) {
|
||||
if (IndirectCase)
|
||||
Data |= IsIndirectCase;
|
||||
else
|
||||
Data &= ~IsIndirectCase;
|
||||
}
|
||||
|
||||
void setIsVar(bool Var=true) {
|
||||
if (Var)
|
||||
Data |= IsVar;
|
||||
else
|
||||
Data &= ~IsVar;
|
||||
}
|
||||
|
||||
int_type getRawValue() const {
|
||||
return Data;
|
||||
}
|
||||
};
|
||||
|
||||
class FieldRecord {
|
||||
const FieldRecordFlags Flags;
|
||||
const RelativeDirectPointer<const char> MangledTypeName;
|
||||
const RelativeDirectPointer<const char> FieldName;
|
||||
|
||||
public:
|
||||
FieldRecord() = delete;
|
||||
|
||||
bool hasMangledTypeName() const {
|
||||
return MangledTypeName;
|
||||
}
|
||||
|
||||
string_view getMangledTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char *)((uintptr_t)MangledTypeName.get() + Offset));
|
||||
}
|
||||
|
||||
string_view getFieldName(uintptr_t Offset) const {
|
||||
if (FieldName)
|
||||
return (const char *)((uintptr_t)FieldName.get() + Offset);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool isIndirectCase() const {
|
||||
return Flags.isIndirectCase();
|
||||
}
|
||||
};
|
||||
|
||||
struct FieldRecordIterator {
|
||||
const FieldRecord *Cur;
|
||||
const FieldRecord * const End;
|
||||
|
||||
FieldRecordIterator(const FieldRecord *Cur, const FieldRecord * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const FieldRecord &operator*() const {
|
||||
return *Cur;
|
||||
}
|
||||
|
||||
const FieldRecord *operator->() const {
|
||||
return Cur;
|
||||
}
|
||||
|
||||
FieldRecordIterator &operator++() {
|
||||
++Cur;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const FieldRecordIterator &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(const FieldRecordIterator &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
enum class FieldDescriptorKind : uint16_t {
|
||||
// Swift nominal types.
|
||||
Struct,
|
||||
Class,
|
||||
Enum,
|
||||
|
||||
// Fixed-size multi-payload enums have a special descriptor format that
|
||||
// encodes spare bits.
|
||||
//
|
||||
// FIXME: Actually implement this. For now, a descriptor with this kind
|
||||
// just means we also have a builtin descriptor from which we get the
|
||||
// size and alignment.
|
||||
MultiPayloadEnum,
|
||||
|
||||
// A Swift opaque protocol. There are no fields, just a record for the
|
||||
// type itself.
|
||||
Protocol,
|
||||
|
||||
// A Swift class-bound protocol.
|
||||
ClassProtocol,
|
||||
|
||||
// An Objective-C protocol, which may be imported or defined in Swift.
|
||||
ObjCProtocol,
|
||||
|
||||
// An Objective-C class, which may be imported or defined in Swift.
|
||||
// In the former case, field type metadata is not emitted, and
|
||||
// must be obtained from the Objective-C runtime.
|
||||
ObjCClass
|
||||
};
|
||||
|
||||
// Field descriptors contain a collection of field records for a single
|
||||
// class, struct or enum declaration.
|
||||
class FieldDescriptor {
|
||||
const FieldRecord *getFieldRecordBuffer() const {
|
||||
return reinterpret_cast<const FieldRecord *>(this + 1);
|
||||
}
|
||||
|
||||
const RelativeDirectPointer<const char> MangledTypeName;
|
||||
const RelativeDirectPointer<const char> Superclass;
|
||||
|
||||
public:
|
||||
FieldDescriptor() = delete;
|
||||
|
||||
const FieldDescriptorKind Kind;
|
||||
const uint16_t FieldRecordSize;
|
||||
const uint32_t NumFields;
|
||||
|
||||
using const_iterator = FieldRecordIterator;
|
||||
|
||||
bool isEnum() const {
|
||||
return (Kind == FieldDescriptorKind::Enum ||
|
||||
Kind == FieldDescriptorKind::MultiPayloadEnum);
|
||||
}
|
||||
|
||||
bool isClass() const {
|
||||
return (Kind == FieldDescriptorKind::Class ||
|
||||
Kind == FieldDescriptorKind::ObjCClass);
|
||||
}
|
||||
|
||||
bool isProtocol() const {
|
||||
return (Kind == FieldDescriptorKind::Protocol ||
|
||||
Kind == FieldDescriptorKind::ClassProtocol ||
|
||||
Kind == FieldDescriptorKind::ObjCProtocol);
|
||||
}
|
||||
|
||||
bool isStruct() const {
|
||||
return Kind == FieldDescriptorKind::Struct;
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
auto Begin = getFieldRecordBuffer();
|
||||
auto End = Begin + NumFields;
|
||||
return const_iterator { Begin, End };
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
auto Begin = getFieldRecordBuffer();
|
||||
auto End = Begin + NumFields;
|
||||
return const_iterator { End, End };
|
||||
}
|
||||
|
||||
span<const FieldRecord> getFields() const {
|
||||
return {getFieldRecordBuffer(), NumFields};
|
||||
}
|
||||
|
||||
bool hasMangledTypeName() const {
|
||||
return MangledTypeName;
|
||||
}
|
||||
|
||||
string_view getMangledTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char *)((uintptr_t)MangledTypeName.get() + Offset));
|
||||
}
|
||||
|
||||
bool hasSuperclass() const {
|
||||
return Superclass;
|
||||
}
|
||||
|
||||
string_view getSuperclass(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)Superclass.get() + Offset));
|
||||
}
|
||||
};
|
||||
|
||||
class FieldDescriptorIterator
|
||||
: public std::iterator<std::forward_iterator_tag, FieldDescriptor> {
|
||||
public:
|
||||
const void *Cur;
|
||||
const void * const End;
|
||||
FieldDescriptorIterator(const void *Cur, const void * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const FieldDescriptor &operator*() const {
|
||||
return *reinterpret_cast<const FieldDescriptor *>(Cur);
|
||||
}
|
||||
|
||||
const FieldDescriptor *operator->() const {
|
||||
return reinterpret_cast<const FieldDescriptor *>(Cur);
|
||||
}
|
||||
|
||||
FieldDescriptorIterator &operator++() {
|
||||
const auto &FR = this->operator*();
|
||||
const void *Next = reinterpret_cast<const char *>(Cur)
|
||||
+ sizeof(FieldDescriptor) + FR.NumFields * FR.FieldRecordSize;
|
||||
Cur = Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(FieldDescriptorIterator const &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(FieldDescriptorIterator const &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
// Associated type records describe the mapping from an associated
|
||||
// type to the type witness of a conformance.
|
||||
class AssociatedTypeRecord {
|
||||
const RelativeDirectPointer<const char> Name;
|
||||
const RelativeDirectPointer<const char> SubstitutedTypeName;
|
||||
|
||||
public:
|
||||
string_view getName(uintptr_t Offset) const {
|
||||
return (const char*)((uintptr_t)Name.get() + Offset);
|
||||
}
|
||||
|
||||
string_view getMangledSubstitutedTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)SubstitutedTypeName.get() + Offset));
|
||||
}
|
||||
};
|
||||
|
||||
struct AssociatedTypeRecordIterator {
|
||||
const AssociatedTypeRecord *Cur;
|
||||
const AssociatedTypeRecord * const End;
|
||||
|
||||
AssociatedTypeRecordIterator()
|
||||
: Cur(nullptr), End(nullptr) {}
|
||||
|
||||
AssociatedTypeRecordIterator(const AssociatedTypeRecord *Cur,
|
||||
const AssociatedTypeRecord * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const AssociatedTypeRecord &operator*() const {
|
||||
return *Cur;
|
||||
}
|
||||
|
||||
const AssociatedTypeRecord *operator->() const {
|
||||
return Cur;
|
||||
}
|
||||
|
||||
AssociatedTypeRecordIterator &operator++() {
|
||||
++Cur;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AssociatedTypeRecordIterator
|
||||
operator=(const AssociatedTypeRecordIterator &Other) {
|
||||
return { Other.Cur, Other.End };
|
||||
}
|
||||
|
||||
bool operator==(const AssociatedTypeRecordIterator &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(const AssociatedTypeRecordIterator &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return Cur && End;
|
||||
}
|
||||
};
|
||||
|
||||
// An associated type descriptor contains a collection of associated
|
||||
// type records for a conformance.
|
||||
struct AssociatedTypeDescriptor {
|
||||
private:
|
||||
const RelativeDirectPointer<const char> ConformingTypeName;
|
||||
const RelativeDirectPointer<const char> ProtocolTypeName;
|
||||
public:
|
||||
|
||||
uint32_t NumAssociatedTypes;
|
||||
uint32_t AssociatedTypeRecordSize;
|
||||
|
||||
const AssociatedTypeRecord *getAssociatedTypeRecordBuffer() const {
|
||||
return reinterpret_cast<const AssociatedTypeRecord *>(this + 1);
|
||||
}
|
||||
|
||||
using const_iterator = AssociatedTypeRecordIterator;
|
||||
|
||||
const_iterator begin() const {
|
||||
auto Begin = getAssociatedTypeRecordBuffer();
|
||||
auto End = Begin + NumAssociatedTypes;
|
||||
return const_iterator { Begin, End };
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
auto Begin = getAssociatedTypeRecordBuffer();
|
||||
auto End = Begin + NumAssociatedTypes;
|
||||
return const_iterator { End, End };
|
||||
}
|
||||
|
||||
string_view getMangledProtocolTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)ProtocolTypeName.get() + Offset));
|
||||
}
|
||||
|
||||
string_view getMangledConformingTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)ConformingTypeName.get() + Offset));
|
||||
}
|
||||
};
|
||||
|
||||
class AssociatedTypeIterator
|
||||
: public std::iterator<std::forward_iterator_tag, AssociatedTypeDescriptor> {
|
||||
public:
|
||||
const void *Cur;
|
||||
const void * const End;
|
||||
AssociatedTypeIterator(const void *Cur, const void * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const AssociatedTypeDescriptor &operator*() const {
|
||||
return *reinterpret_cast<const AssociatedTypeDescriptor *>(Cur);
|
||||
}
|
||||
|
||||
const AssociatedTypeDescriptor *operator->() const {
|
||||
return reinterpret_cast<const AssociatedTypeDescriptor *>(Cur);
|
||||
}
|
||||
|
||||
AssociatedTypeIterator &operator++() {
|
||||
const auto &ATR = this->operator*();
|
||||
size_t Size = sizeof(AssociatedTypeDescriptor) +
|
||||
ATR.NumAssociatedTypes * ATR.AssociatedTypeRecordSize;
|
||||
const void *Next = reinterpret_cast<const char *>(Cur) + Size;
|
||||
Cur = Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(AssociatedTypeIterator const &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(AssociatedTypeIterator const &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
// Builtin type records describe basic layout information about
|
||||
// any builtin types referenced from the other sections.
|
||||
class BuiltinTypeDescriptor {
|
||||
const RelativeDirectPointer<const char> TypeName;
|
||||
|
||||
public:
|
||||
uint32_t Size;
|
||||
|
||||
// - Least significant 16 bits are the alignment.
|
||||
// - Bit 16 is 'bitwise takable'.
|
||||
// - Remaining bits are reserved.
|
||||
uint32_t AlignmentAndFlags;
|
||||
|
||||
uint32_t Stride;
|
||||
uint32_t NumExtraInhabitants;
|
||||
|
||||
bool isBitwiseTakable() const {
|
||||
return (AlignmentAndFlags >> 16) & 1;
|
||||
}
|
||||
|
||||
uint32_t getAlignment() const {
|
||||
return AlignmentAndFlags & 0xffff;
|
||||
}
|
||||
|
||||
bool hasMangledTypeName() const {
|
||||
return TypeName;
|
||||
}
|
||||
|
||||
string_view getMangledTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)TypeName.get() + Offset));
|
||||
}
|
||||
};
|
||||
|
||||
class BuiltinTypeDescriptorIterator
|
||||
: public std::iterator<std::forward_iterator_tag, BuiltinTypeDescriptor> {
|
||||
public:
|
||||
const void *Cur;
|
||||
const void * const End;
|
||||
BuiltinTypeDescriptorIterator(const void *Cur, const void * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const BuiltinTypeDescriptor &operator*() const {
|
||||
return *reinterpret_cast<const BuiltinTypeDescriptor *>(Cur);
|
||||
}
|
||||
|
||||
const BuiltinTypeDescriptor *operator->() const {
|
||||
return reinterpret_cast<const BuiltinTypeDescriptor *>(Cur);;
|
||||
}
|
||||
|
||||
BuiltinTypeDescriptorIterator &operator++() {
|
||||
const void *Next = reinterpret_cast<const char *>(Cur)
|
||||
+ sizeof(BuiltinTypeDescriptor);
|
||||
Cur = Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(BuiltinTypeDescriptorIterator const &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(BuiltinTypeDescriptorIterator const &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
class CaptureTypeRecord {
|
||||
const RelativeDirectPointer<const char> MangledTypeName;
|
||||
|
||||
public:
|
||||
CaptureTypeRecord() = delete;
|
||||
|
||||
bool hasMangledTypeName() const {
|
||||
return MangledTypeName;
|
||||
}
|
||||
|
||||
string_view getMangledTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)MangledTypeName.get() + Offset));
|
||||
}
|
||||
};
|
||||
|
||||
struct CaptureTypeRecordIterator {
|
||||
const CaptureTypeRecord *Cur;
|
||||
const CaptureTypeRecord * const End;
|
||||
|
||||
CaptureTypeRecordIterator(const CaptureTypeRecord *Cur,
|
||||
const CaptureTypeRecord * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const CaptureTypeRecord &operator*() const {
|
||||
return *Cur;
|
||||
}
|
||||
|
||||
const CaptureTypeRecord *operator->() const {
|
||||
return Cur;
|
||||
}
|
||||
|
||||
CaptureTypeRecordIterator &operator++() {
|
||||
++Cur;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const CaptureTypeRecordIterator &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(const CaptureTypeRecordIterator &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
class MetadataSourceRecord {
|
||||
const RelativeDirectPointer<const char> MangledTypeName;
|
||||
const RelativeDirectPointer<const char> MangledMetadataSource;
|
||||
|
||||
public:
|
||||
MetadataSourceRecord() = delete;
|
||||
|
||||
bool hasMangledTypeName() const {
|
||||
return MangledTypeName;
|
||||
}
|
||||
|
||||
string_view getMangledTypeName(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)MangledTypeName.get() + Offset));
|
||||
}
|
||||
|
||||
bool hasMangledMetadataSource() const {
|
||||
return MangledMetadataSource;
|
||||
}
|
||||
|
||||
string_view getMangledMetadataSource(uintptr_t Offset) const {
|
||||
return Demangle::makeSymbolicMangledNameStringRef(
|
||||
(const char*)((uintptr_t)MangledMetadataSource.get() + Offset));
|
||||
}
|
||||
};
|
||||
|
||||
struct MetadataSourceRecordIterator {
|
||||
const MetadataSourceRecord *Cur;
|
||||
const MetadataSourceRecord * const End;
|
||||
|
||||
MetadataSourceRecordIterator(const MetadataSourceRecord *Cur,
|
||||
const MetadataSourceRecord * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const MetadataSourceRecord &operator*() const {
|
||||
return *Cur;
|
||||
}
|
||||
|
||||
const MetadataSourceRecord *operator->() const {
|
||||
return Cur;
|
||||
}
|
||||
|
||||
MetadataSourceRecordIterator &operator++() {
|
||||
++Cur;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const MetadataSourceRecordIterator &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(const MetadataSourceRecordIterator &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
// Capture descriptors describe the layout of a closure context
|
||||
// object. Unlike nominal types, the generic substitutions for a
|
||||
// closure context come from the object, and not the metadata.
|
||||
class CaptureDescriptor {
|
||||
const CaptureTypeRecord *getCaptureTypeRecordBuffer() const {
|
||||
return reinterpret_cast<const CaptureTypeRecord *>(this + 1);
|
||||
}
|
||||
|
||||
const MetadataSourceRecord *getMetadataSourceRecordBuffer() const {
|
||||
return reinterpret_cast<const MetadataSourceRecord *>(capture_end().End);
|
||||
}
|
||||
|
||||
public:
|
||||
/// The number of captures in the closure and the number of typerefs that
|
||||
/// immediately follow this struct.
|
||||
uint32_t NumCaptureTypes;
|
||||
|
||||
/// The number of sources of metadata available in the MetadataSourceMap
|
||||
/// directly following the list of capture's typerefs.
|
||||
uint32_t NumMetadataSources;
|
||||
|
||||
/// The number of items in the NecessaryBindings structure at the head of
|
||||
/// the closure.
|
||||
uint32_t NumBindings;
|
||||
|
||||
using const_iterator = FieldRecordIterator;
|
||||
|
||||
CaptureTypeRecordIterator capture_begin() const {
|
||||
auto Begin = getCaptureTypeRecordBuffer();
|
||||
auto End = Begin + NumCaptureTypes;
|
||||
return { Begin, End };
|
||||
}
|
||||
|
||||
CaptureTypeRecordIterator capture_end() const {
|
||||
auto Begin = getCaptureTypeRecordBuffer();
|
||||
auto End = Begin + NumCaptureTypes;
|
||||
return { End, End };
|
||||
}
|
||||
|
||||
MetadataSourceRecordIterator source_begin() const {
|
||||
auto Begin = getMetadataSourceRecordBuffer();
|
||||
auto End = Begin + NumMetadataSources;
|
||||
return { Begin, End };
|
||||
}
|
||||
|
||||
MetadataSourceRecordIterator source_end() const {
|
||||
auto Begin = getMetadataSourceRecordBuffer();
|
||||
auto End = Begin + NumMetadataSources;
|
||||
return { End, End };
|
||||
}
|
||||
};
|
||||
|
||||
class CaptureDescriptorIterator
|
||||
: public std::iterator<std::forward_iterator_tag, CaptureDescriptor> {
|
||||
public:
|
||||
const void *Cur;
|
||||
const void * const End;
|
||||
CaptureDescriptorIterator(const void *Cur, const void * const End)
|
||||
: Cur(Cur), End(End) {}
|
||||
|
||||
const CaptureDescriptor &operator*() const {
|
||||
return *reinterpret_cast<const CaptureDescriptor *>(Cur);
|
||||
}
|
||||
|
||||
const CaptureDescriptor *operator->() const {
|
||||
return reinterpret_cast<const CaptureDescriptor *>(Cur);
|
||||
}
|
||||
|
||||
CaptureDescriptorIterator &operator++() {
|
||||
const auto &CR = this->operator*();
|
||||
const void *Next = reinterpret_cast<const char *>(Cur)
|
||||
+ sizeof(CaptureDescriptor)
|
||||
+ CR.NumCaptureTypes * sizeof(CaptureTypeRecord)
|
||||
+ CR.NumMetadataSources * sizeof(MetadataSourceRecord);
|
||||
Cur = Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(CaptureDescriptorIterator const &other) const {
|
||||
return Cur == other.Cur && End == other.End;
|
||||
}
|
||||
|
||||
bool operator!=(CaptureDescriptorIterator const &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace reflection
|
||||
} // end namespace swift
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_SWIFT_REFLECTION_RECORDS_H
|
||||
@@ -1,73 +0,0 @@
|
||||
//===--- Config.h - Swift Language Platform Configuration -------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Definitions of common interest in Swift.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// Some macros have been renamed or removed.
|
||||
|
||||
#ifndef OPENCOMBINE_SWIFT_RUNTIME_CONFIG_H
|
||||
#define OPENCOMBINE_SWIFT_RUNTIME_CONFIG_H
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define OPENCOMBINE_SWIFT_RUNTIME_ATTRIBUTE_NORETURN __attribute__((noreturn))
|
||||
#elif defined(_MSC_VER)
|
||||
#define OPENCOMBINE_SWIFT_RUNTIME_ATTRIBUTE_NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define OPENCOMBINE_SWIFT_RUNTIME_ATTRIBUTE_NORETURN
|
||||
#endif
|
||||
|
||||
/// Does the current Swift platform support "unbridged" interoperation
|
||||
/// with Objective-C? If so, the implementations of various types must
|
||||
/// implicitly handle Objective-C pointers.
|
||||
///
|
||||
/// Apple platforms support this by default.
|
||||
#ifndef OPENCOMBINE_SWIFT_OBJC_INTEROP
|
||||
#ifdef __APPLE__
|
||||
#define OPENCOMBINE_SWIFT_OBJC_INTEROP 1
|
||||
#else
|
||||
#define OPENCOMBINE_SWIFT_OBJC_INTEROP 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Which bits in the class metadata are used to distinguish Swift classes
|
||||
/// from ObjC classes?
|
||||
#ifndef OPENCOMBINE_SWIFT_CLASS_IS_SWIFT_MASK
|
||||
|
||||
# if !defined(__APPLE__)
|
||||
// Non-Apple platforms always use 1.
|
||||
# define OPENCOMBINE_SWIFT_CLASS_IS_SWIFT_MASK 1ULL
|
||||
# else
|
||||
// Apple platforms with Swift in the OS (a.k.a. post-ABI-stability) use 2.
|
||||
namespace opencombine { extern unsigned long long classIsSwiftMask; }
|
||||
# define OPENCOMBINE_SWIFT_CLASS_IS_SWIFT_MASK classIsSwiftMask
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Define mappings for calling conventions.
|
||||
|
||||
#if __has_attribute(swiftcall)
|
||||
# define OPENCOMBINE_SWIFT_CALLING_CONVENTION __attribute__((swiftcall))
|
||||
# define OPENCOMBINE_SWIFT_CONTEXT __attribute__((swift_context))
|
||||
# define OPENCOMBINE_SWIFT_ERROR_RESULT __attribute__((swift_error_result))
|
||||
# define OPENCOMBINE_SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
|
||||
#else
|
||||
# define OPENCOMBINE_SWIFT_CALLING_CONVENTION
|
||||
# define OPENCOMBINE_SWIFT_CONTEXT
|
||||
# define OPENCOMBINE_SWIFT_ERROR_RESULT
|
||||
# define OPENCOMBINE_SWIFT_INDIRECT_RESULT
|
||||
#endif
|
||||
|
||||
#endif // OPENCOMBINE_SWIFT_RUNTIME_CONFIG_H
|
||||
@@ -1,46 +0,0 @@
|
||||
//===--- Metadata.h - Swift Language ABI Metadata Support -------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Swift runtime support for generating and uniquing metadata.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef OPENCOMBINE_SWIFT_RUNTIME_METADATA_H
|
||||
#define OPENCOMBINE_SWIFT_RUNTIME_METADATA_H
|
||||
|
||||
#include "swift/ABI/Metadata.h"
|
||||
#include "swift/Reflection/Records.h"
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - Some declarations have been removed.
|
||||
// - The swift namespace is wrapped in the opencombine namespace.
|
||||
// - Replaced ArrayRef and StringRef with span and string_view
|
||||
|
||||
namespace opencombine {
|
||||
namespace swift {
|
||||
|
||||
/// Compute the bounds of class metadata with a resilient superclass.
|
||||
ClassMetadataBounds getResilientMetadataBounds(
|
||||
const ClassDescriptor *descriptor);
|
||||
int32_t getResilientImmediateMembersOffset(const ClassDescriptor *descriptor);
|
||||
|
||||
#if OPENCOMBINE_SWIFT_OBJC_INTEROP
|
||||
|
||||
extern "C" Class swift_getInitializedObjCClass(Class c);
|
||||
|
||||
#endif
|
||||
|
||||
} // end namespace swift
|
||||
} // end namespace opencombine
|
||||
|
||||
#endif // OPENCOMBINE_SWIFT_RUNTIME_METADATA_H
|
||||
@@ -1,37 +0,0 @@
|
||||
//===--- Unreachable.h - Implements swift_runtime_unreachable ---*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines swift_runtime_unreachable, an LLVM-independent
|
||||
// implementation of llvm_unreachable.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MODIFICATION NOTE:
|
||||
// This file has been modified for the OpenCombine open source project.
|
||||
// - Symbols have been prefix with the 'opencombine' prefix.
|
||||
|
||||
#ifndef OPENCOMBINE_SWIFT_RUNTIME_UNREACHABLE_H
|
||||
#define OPENCOMBINE_SWIFT_RUNTIME_UNREACHABLE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "swift/Runtime/Config.h"
|
||||
|
||||
OPENCOMBINE_SWIFT_RUNTIME_ATTRIBUTE_NORETURN
|
||||
inline static void opencombine_swift_runtime_unreachable(const char *msg) {
|
||||
assert(false && msg);
|
||||
(void)msg;
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif // OPENCOMBINE_SWIFT_RUNTIME_UNREACHABLE_H
|
||||
@@ -10,7 +10,8 @@
|
||||
/// Subscriber implementations can use this type to provide a “cancellation token” that
|
||||
/// makes it possible for a caller to cancel a publisher, but not to use the
|
||||
/// `Subscription` object to request items.
|
||||
/// An AnyCancellable instance automatically calls `cancel()` when deinitialized.
|
||||
///
|
||||
/// An `AnyCancellable` instance automatically calls `cancel()` when deinitialized.
|
||||
public final class AnyCancellable: Cancellable, Hashable {
|
||||
|
||||
private var _cancel: (() -> Void)?
|
||||
@@ -46,18 +47,18 @@ public final class AnyCancellable: Cancellable, Hashable {
|
||||
|
||||
extension AnyCancellable {
|
||||
|
||||
/// Stores this AnyCancellable in the specified collection.
|
||||
/// Parameters:
|
||||
/// - collection: The collection to store this AnyCancellable.
|
||||
/// Stores this type-erasing cancellable instance in the specified collection.
|
||||
///
|
||||
/// - Parameter collection: The collection in which to store this `AnyCancellable`.
|
||||
public func store<Cancellables: RangeReplaceableCollection>(
|
||||
in collection: inout Cancellables
|
||||
) where Cancellables.Element == AnyCancellable {
|
||||
collection.append(self)
|
||||
}
|
||||
|
||||
/// Stores this AnyCancellable in the specified set.
|
||||
/// Parameters:
|
||||
/// - collection: The set to store this AnyCancellable.
|
||||
/// Stores this type-erasing cancellable instance in the specified collection.
|
||||
///
|
||||
/// - Parameter collection: The collection in which to store this `AnyCancellable`.
|
||||
public func store(in set: inout Set<AnyCancellable>) {
|
||||
set.insert(self)
|
||||
}
|
||||
|
||||
@@ -9,8 +9,44 @@ extension Publisher {
|
||||
|
||||
/// Wraps this publisher with a type eraser.
|
||||
///
|
||||
/// Use `eraseToAnyPublisher()` to expose an instance of `AnyPublisher` to
|
||||
/// Use `eraseToAnyPublisher()` to expose an instance of `AnyPublisher`` to
|
||||
/// the downstream subscriber, rather than this publisher’s actual type.
|
||||
/// This form of _type erasure_ preserves abstraction across API boundaries, such as
|
||||
/// different modules.
|
||||
/// When you expose your publishers as the `AnyPublisher` type, you can change
|
||||
/// the underlying implementation over time without affecting existing clients.
|
||||
///
|
||||
/// The following example shows two types that each have a `publisher` property.
|
||||
/// `TypeWithSubject` exposes this property as its actual type, `PassthroughSubject`,
|
||||
/// while `TypeWithErasedSubject` uses `eraseToAnyPublisher()` to expose it as
|
||||
/// an `AnyPublisher`. As seen in the output, a caller from another module can access
|
||||
/// `TypeWithSubject.publisher` as its native type. This means you can’t change your
|
||||
/// publisher to a different type without breaking the caller. By comparison,
|
||||
/// `TypeWithErasedSubject.publisher` appears to callers as an `AnyPublisher`, so you
|
||||
/// can change the underlying publisher type at will.
|
||||
///
|
||||
/// public class TypeWithSubject {
|
||||
/// public let publisher: some Publisher = PassthroughSubject<Int,Never>()
|
||||
/// }
|
||||
/// public class TypeWithErasedSubject {
|
||||
/// public let publisher: some Publisher = PassthroughSubject<Int,Never>()
|
||||
/// .eraseToAnyPublisher()
|
||||
/// }
|
||||
///
|
||||
/// // In another module:
|
||||
/// let nonErased = TypeWithSubject()
|
||||
/// if let subject = nonErased.publisher as? PassthroughSubject<Int,Never> {
|
||||
/// print("Successfully cast nonErased.publisher.")
|
||||
/// }
|
||||
/// let erased = TypeWithErasedSubject()
|
||||
/// if let subject = erased.publisher as? PassthroughSubject<Int,Never> {
|
||||
/// print("Successfully cast erased.publisher.")
|
||||
/// }
|
||||
///
|
||||
/// // Prints "Successfully cast nonErased.publisher."
|
||||
///
|
||||
/// - Returns: An ``AnyPublisher`` wrapping this publisher.
|
||||
@inlinable
|
||||
public func eraseToAnyPublisher() -> AnyPublisher<Output, Failure> {
|
||||
return .init(self)
|
||||
}
|
||||
@@ -19,7 +55,13 @@ extension Publisher {
|
||||
/// A type-erasing publisher.
|
||||
///
|
||||
/// Use `AnyPublisher` to wrap a publisher whose type has details you don’t want to expose
|
||||
/// to subscribers or other publishers.
|
||||
/// across API boundaries, such as different modules. Wrapping a `Subject` with
|
||||
/// `AnyPublisher` also prevents callers from accessing its `send(_:)` method. When you
|
||||
/// use type erasure this way, you can change the underlying publisher implementation over
|
||||
/// time without affecting existing clients.
|
||||
///
|
||||
/// You can use OpenCombine’s `eraseToAnyPublisher()` operator to wrap a publisher with
|
||||
/// `AnyPublisher`.
|
||||
public struct AnyPublisher<Output, Failure: Error>
|
||||
: CustomStringConvertible,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
@@ -29,13 +71,17 @@ public struct AnyPublisher<Output, Failure: Error>
|
||||
|
||||
/// Creates a type-erasing publisher to wrap the provided publisher.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - publisher: A publisher to wrap with a type-eraser.
|
||||
/// - Parameter publisher: A publisher to wrap with a type-eraser.
|
||||
@inlinable
|
||||
public init<PublisherType: Publisher>(_ publisher: PublisherType)
|
||||
where Output == PublisherType.Output, Failure == PublisherType.Failure
|
||||
{
|
||||
box = PublisherBox(base: publisher)
|
||||
// If this has already been boxed, avoid boxing again
|
||||
if let erased = publisher as? AnyPublisher<Output, Failure> {
|
||||
box = erased.box
|
||||
} else {
|
||||
box = PublisherBox(base: publisher)
|
||||
}
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
@@ -60,7 +106,7 @@ extension AnyPublisher: Publisher {
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Output == Downstream.Input, Failure == Downstream.Failure
|
||||
{
|
||||
box.subscribe(subscriber)
|
||||
box.receive(subscriber: subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +143,6 @@ internal final class PublisherBox<PublisherType: Publisher>
|
||||
override internal func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Failure == Downstream.Failure, Output == Downstream.Input
|
||||
{
|
||||
base.subscribe(subscriber)
|
||||
base.receive(subscriber: subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
|
||||
/// A type-erasing subscriber.
|
||||
///
|
||||
/// Use an `AnySubscriber` to wrap an existing subscriber whose details you don’t want
|
||||
/// to expose. You can also use `AnySubscriber` to create a custom subscriber by providing
|
||||
/// closures for `Subscriber`’s methods, rather than implementing `Subscriber` directly.
|
||||
/// Use an `AnySubscriber` to wrap an existing subscriber whose details you don’t want to
|
||||
/// expose. You can also use `AnySubscriber` to create a custom subscriber by providing
|
||||
/// closures for the methods defined in `Subscriber`, rather than implementing
|
||||
/// `Subscriber` directly.
|
||||
public struct AnySubscriber<Input, Failure: Error>: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
@@ -44,6 +45,11 @@ public struct AnySubscriber<Input, Failure: Error>: Subscriber,
|
||||
public init<Subscriber: OpenCombine.Subscriber>(_ subscriber: Subscriber)
|
||||
where Input == Subscriber.Input, Failure == Subscriber.Failure
|
||||
{
|
||||
if let erased = subscriber as? AnySubscriber<Input, Failure> {
|
||||
self = erased
|
||||
return
|
||||
}
|
||||
|
||||
combineIdentifier = subscriber.combineIdentifier
|
||||
|
||||
box = AnySubscriberBox(subscriber)
|
||||
@@ -62,8 +68,8 @@ public struct AnySubscriber<Input, Failure: Error>: Subscriber,
|
||||
|
||||
if let playgroundDescription = subscriber as? CustomPlaygroundDisplayConvertible {
|
||||
playgroundDescriptionThunk = { playgroundDescription.playgroundDescription }
|
||||
} else if let desccription = subscriber as? CustomStringConvertible {
|
||||
playgroundDescriptionThunk = { desccription.description }
|
||||
} else if let description = subscriber as? CustomStringConvertible {
|
||||
playgroundDescriptionThunk = { description.description }
|
||||
} else {
|
||||
let fixedDescription = String(describing: type(of: subscriber))
|
||||
playgroundDescriptionThunk = { fixedDescription }
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// Created by Sergej Jaskiewicz on 10.06.2019.
|
||||
//
|
||||
|
||||
/// A protocol indicating that an activity or action may be canceled.
|
||||
/// A protocol indicating that an activity or action supports cancellation.
|
||||
///
|
||||
/// Calling `cancel()` frees up any allocated resources. It also stops side effects such
|
||||
/// as timers, network access, or disk I/O.
|
||||
@@ -17,18 +17,18 @@ public protocol Cancellable {
|
||||
|
||||
extension Cancellable {
|
||||
|
||||
/// Stores this Cancellable in the specified collection.
|
||||
/// Parameters:
|
||||
/// - collection: The collection to store this Cancellable.
|
||||
/// Stores this cancellable instance in the specified collection.
|
||||
///
|
||||
/// - Parameter collection: The collection in which to store this `Cancellable`.
|
||||
public func store<Cancellables: RangeReplaceableCollection>(
|
||||
in collection: inout Cancellables
|
||||
) where Cancellables.Element == AnyCancellable {
|
||||
AnyCancellable(self).store(in: &collection)
|
||||
}
|
||||
|
||||
/// Stores this Cancellable in the specified set.
|
||||
/// Parameters:
|
||||
/// - collection: The set to store this Cancellable.
|
||||
/// Stores this cancellable instance in the specified set.
|
||||
///
|
||||
/// - Parameter set: The set in which to store this `Cancellable`.
|
||||
public func store(in set: inout Set<AnyCancellable>) {
|
||||
AnyCancellable(self).store(in: &set)
|
||||
}
|
||||
|
||||
@@ -5,17 +5,25 @@
|
||||
// Created by Sergej Jaskiewicz on 11.06.2019.
|
||||
//
|
||||
|
||||
/// A type that defines methods for decoding.
|
||||
public protocol TopLevelDecoder {
|
||||
|
||||
/// The type this decoder accepts.
|
||||
associatedtype Input
|
||||
|
||||
/// Decodes an instance of the indicated type.
|
||||
func decode<DecodablyType: Decodable>(_ type: DecodablyType.Type,
|
||||
from: Input) throws -> DecodablyType
|
||||
}
|
||||
|
||||
/// A type that defines methods for encoding.
|
||||
public protocol TopLevelEncoder {
|
||||
|
||||
/// The type this encoder produces.
|
||||
associatedtype Output
|
||||
|
||||
/// Encodes an instance of the indicated type.
|
||||
///
|
||||
/// - Parameter value: The instance to encode.
|
||||
func encode<EncodableType: Encodable>(_ value: EncodableType) throws -> Output
|
||||
}
|
||||
|
||||
@@ -5,21 +5,41 @@
|
||||
// Created by Sergej Jaskiewicz on 10.06.2019.
|
||||
//
|
||||
|
||||
import func COpenCombineHelpers.nextCombineIdentifier
|
||||
#if canImport(COpenCombineHelpers)
|
||||
import COpenCombineHelpers
|
||||
#endif
|
||||
|
||||
#if WASI
|
||||
private var __identifier: UInt64 = 0
|
||||
|
||||
internal func __nextCombineIdentifier() -> UInt64 {
|
||||
defer { __identifier += 1 }
|
||||
return __identifier
|
||||
}
|
||||
#endif // WASI
|
||||
|
||||
/// A unique identifier for identifying publisher streams.
|
||||
///
|
||||
/// To conform to `CustomCombineIdentifierConvertible` in a
|
||||
/// `Subscription` or `Subject` that you implement as a structure, create an instance of
|
||||
/// `CombineIdentifier` as follows:
|
||||
///
|
||||
/// let combineIdentifier = CombineIdentifier()
|
||||
public struct CombineIdentifier: Hashable, CustomStringConvertible {
|
||||
|
||||
private let value: UInt64
|
||||
private let rawValue: UInt64
|
||||
|
||||
/// Creates a unique Combine identifier.
|
||||
public init() {
|
||||
value = nextCombineIdentifier()
|
||||
rawValue = __nextCombineIdentifier()
|
||||
}
|
||||
|
||||
/// Creates a Combine identifier, using the bit pattern of the provided object.
|
||||
public init(_ obj: AnyObject) {
|
||||
value = UInt64(UInt(bitPattern: ObjectIdentifier(obj)))
|
||||
rawValue = UInt64(UInt(bitPattern: ObjectIdentifier(obj)))
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return "0x\(String(value, radix: 16))"
|
||||
return "0x\(String(rawValue, radix: 16))"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,18 @@
|
||||
|
||||
/// A publisher that provides an explicit means of connecting and canceling publication.
|
||||
///
|
||||
/// Use a `ConnectablePublisher` when you need to perform additional configuration or
|
||||
/// setup prior to producing any elements.
|
||||
///
|
||||
/// This publisher doesn’t produce any elements until you call its `connect()` method.
|
||||
///
|
||||
/// Use `makeConnectable()` to create a `ConnectablePublisher` from any publisher whose
|
||||
/// failure type is `Never`.
|
||||
public protocol ConnectablePublisher: Publisher {
|
||||
|
||||
/// Connects to the publisher and returns a `Cancellable` instance with which
|
||||
/// to cancel publishing.
|
||||
/// Connects to the publisher, allowing it to produce elements, and returns
|
||||
/// an instance with which to cancel publishing.
|
||||
///
|
||||
/// - Returns: A `Cancellable` instance that can be used to cancel publishing.
|
||||
/// - Returns: A `Cancellable` instance that you use to cancel publishing.
|
||||
func connect() -> Cancellable
|
||||
}
|
||||
|
||||
@@ -5,32 +5,39 @@
|
||||
// Created by Sergej Jaskiewicz on 11.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
/// A subject that wraps a single value and publishes a new element whenever the value
|
||||
/// changes.
|
||||
///
|
||||
/// Unlike `PassthroughSubject`, `CurrentValueSubject` maintains a buffer of the most
|
||||
/// recently published element.
|
||||
///
|
||||
/// Calling `send(_:)` on a `CurrentValueSubject` also updates the current value, making
|
||||
/// it equivalent to updating the `value` directly.
|
||||
public final class CurrentValueSubject<Output, Failure: Error>: Subject {
|
||||
|
||||
private let _lock = UnfairRecursiveLock.allocate()
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
// TODO: Combine uses bag data structure
|
||||
private var _subscriptions: [Conduit] = []
|
||||
private var active = true
|
||||
|
||||
private var _value: Output
|
||||
private var completion: Subscribers.Completion<Failure>?
|
||||
|
||||
private var _completion: Subscribers.Completion<Failure>?
|
||||
private var downstreams = ConduitList<Output, Failure>.empty
|
||||
|
||||
internal var upstreamSubscriptions: [Subscription] = []
|
||||
private var currentValue: Output
|
||||
|
||||
internal var hasAnyDownstreamDemand = false
|
||||
private var upstreamSubscriptions: [Subscription] = []
|
||||
|
||||
/// The value wrapped by this subject, published as a new element whenever it changes.
|
||||
public var value: Output {
|
||||
get {
|
||||
return _value
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return currentValue
|
||||
}
|
||||
set {
|
||||
send(newValue)
|
||||
lock.lock()
|
||||
currentValue = newValue
|
||||
sendValueAndConsumeLock(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,122 +45,213 @@ public final class CurrentValueSubject<Output, Failure: Error>: Subject {
|
||||
///
|
||||
/// - Parameter value: The initial value to publish.
|
||||
public init(_ value: Output) {
|
||||
self._value = value
|
||||
self.currentValue = value
|
||||
}
|
||||
|
||||
deinit {
|
||||
for subscription in _subscriptions {
|
||||
subscription._downstream = nil
|
||||
for subscription in upstreamSubscriptions {
|
||||
subscription.cancel()
|
||||
}
|
||||
_lock.deallocate()
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
public func send(subscription: Subscription) {
|
||||
_lock.do {
|
||||
upstreamSubscriptions.append(subscription)
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
lock.lock()
|
||||
upstreamSubscriptions.append(subscription)
|
||||
lock.unlock()
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
|
||||
public func receive<Subscriber: OpenCombine.Subscriber>(subscriber: Subscriber)
|
||||
where Output == Subscriber.Input, Failure == Subscriber.Failure
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Output == Downstream.Input, Failure == Downstream.Failure
|
||||
{
|
||||
_lock.do {
|
||||
|
||||
if let completion = _completion {
|
||||
subscriber.receive(subscription: Subscriptions.empty)
|
||||
subscriber.receive(completion: completion)
|
||||
return
|
||||
} else {
|
||||
let subscription = Conduit(parent: self,
|
||||
downstream: AnySubscriber(subscriber))
|
||||
|
||||
_subscriptions.append(subscription)
|
||||
subscriber.receive(subscription: subscription)
|
||||
}
|
||||
lock.lock()
|
||||
if active {
|
||||
let conduit = Conduit(parent: self, downstream: subscriber)
|
||||
downstreams.insert(conduit)
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: conduit)
|
||||
} else {
|
||||
let completion = self.completion!
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: Subscriptions.empty)
|
||||
subscriber.receive(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public func send(_ input: Output) {
|
||||
_lock.do {
|
||||
_value = input
|
||||
for subscription in _subscriptions where !subscription.isCompleted {
|
||||
if subscription._demand > 0 {
|
||||
subscription._offer(input)
|
||||
subscription._demand -= 1
|
||||
} else {
|
||||
subscription._delivered = false
|
||||
}
|
||||
}
|
||||
lock.lock()
|
||||
sendValueAndConsumeLock(input)
|
||||
}
|
||||
|
||||
private func sendValueAndConsumeLock(_ newValue: Output) {
|
||||
#if DEBUG
|
||||
lock.assertOwner()
|
||||
#endif
|
||||
guard active else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
currentValue = newValue
|
||||
let downstreams = self.downstreams
|
||||
lock.unlock()
|
||||
downstreams.forEach { conduit in
|
||||
conduit.offer(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
public func send(completion: Subscribers.Completion<Failure>) {
|
||||
_completion = completion
|
||||
_lock.do {
|
||||
for subscriber in _subscriptions {
|
||||
subscriber._receive(completion: completion)
|
||||
}
|
||||
lock.lock()
|
||||
guard active else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
active = false
|
||||
self.completion = completion
|
||||
let downstreams = self.downstreams
|
||||
self.downstreams.removeAll()
|
||||
lock.unlock()
|
||||
downstreams.forEach { conduit in
|
||||
conduit.finish(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
private func disassociate(_ conduit: ConduitBase<Output, Failure>) {
|
||||
lock.lock()
|
||||
guard active else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
downstreams.remove(conduit)
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
extension CurrentValueSubject {
|
||||
|
||||
fileprivate class Conduit: Subscription {
|
||||
private final class Conduit<Downstream: Subscriber>
|
||||
: ConduitBase<Output, Failure>,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
|
||||
fileprivate var _parent: CurrentValueSubject?
|
||||
fileprivate var parent: CurrentValueSubject?
|
||||
|
||||
fileprivate var _downstream: AnySubscriber<Output, Failure>?
|
||||
fileprivate var downstream: Downstream?
|
||||
|
||||
fileprivate var _demand: Subscribers.Demand = .none
|
||||
fileprivate var demand = Subscribers.Demand.none
|
||||
|
||||
/// Whethere we satisfied the demand
|
||||
fileprivate var _delivered = false
|
||||
private var lock = UnfairLock.allocate()
|
||||
|
||||
var isCompleted: Bool {
|
||||
return _parent == nil
|
||||
}
|
||||
private var downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
fileprivate func _offer(_ value: Output) {
|
||||
let newDemand = _downstream?.receive(value) ?? .none
|
||||
_demand += newDemand
|
||||
_delivered = true
|
||||
}
|
||||
private var deliveredCurrentValue = false
|
||||
|
||||
fileprivate init(parent: CurrentValueSubject,
|
||||
downstream: AnySubscriber<Output, Failure>) {
|
||||
_parent = parent
|
||||
_downstream = downstream
|
||||
downstream: Downstream) {
|
||||
self.parent = parent
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
fileprivate func _receive(completion: Subscribers.Completion<Failure>) {
|
||||
if !isCompleted {
|
||||
_parent = nil
|
||||
_downstream?.receive(completion: completion)
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
override func offer(_ output: Output) {
|
||||
lock.lock()
|
||||
guard demand > 0, let downstream = self.downstream else {
|
||||
deliveredCurrentValue = false
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
demand -= 1
|
||||
deliveredCurrentValue = true
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(output)
|
||||
downstreamLock.unlock()
|
||||
guard newDemand > 0 else { return }
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
precondition(demand > 0)
|
||||
_parent?._lock.do {
|
||||
if !_delivered, let value = _parent?.value {
|
||||
_offer(value)
|
||||
_demand += demand
|
||||
_demand -= 1
|
||||
} else {
|
||||
_demand = demand
|
||||
}
|
||||
_parent?.hasAnyDownstreamDemand = true
|
||||
override func finish(completion: Subscribers.Completion<Failure>) {
|
||||
lock.lock()
|
||||
guard let downstream = self.downstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.downstream = nil
|
||||
let parent = self.parent
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
parent?.disassociate(self)
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
_parent = nil
|
||||
override func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
guard let downstream = self.downstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
if deliveredCurrentValue {
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Hasn't yet delivered the current value
|
||||
|
||||
self.demand += demand
|
||||
deliveredCurrentValue = true
|
||||
if let currentValue = self.parent?.value {
|
||||
self.demand -= 1
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(currentValue)
|
||||
downstreamLock.unlock()
|
||||
guard newDemand > 0 else { return }
|
||||
lock.lock()
|
||||
self.demand += newDemand
|
||||
}
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
override func cancel() {
|
||||
lock.lock()
|
||||
if self.downstream == nil {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.downstream = nil
|
||||
let parent = self.parent
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
parent?.disassociate(self)
|
||||
}
|
||||
|
||||
var description: String { return "CurrentValueSubject" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("parent", parent as Any),
|
||||
("downstream", downstream as Any),
|
||||
("demand", demand),
|
||||
("subject", parent as Any)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
extension CurrentValueSubject.Conduit: CustomStringConvertible {
|
||||
fileprivate var description: String { return "CurrentValueSubject" }
|
||||
}
|
||||
|
||||
@@ -5,8 +5,18 @@
|
||||
// Created by Sergej Jaskiewicz on 10.06.2019.
|
||||
//
|
||||
|
||||
/// A protocol for uniquely identifying publisher streams.
|
||||
///
|
||||
/// If you create a custom `Subscription` or `Subscriber` type, implement this protocol
|
||||
/// so that development tools can uniquely identify publisher chains in your app.
|
||||
/// If your type is a class, OpenCombine provides an implementation of `combineIdentifier`
|
||||
/// for you.
|
||||
/// If your type is a structure, set up the identifier as follows:
|
||||
///
|
||||
/// let combineIdentifier = CombineIdentifier()
|
||||
public protocol CustomCombineIdentifierConvertible {
|
||||
|
||||
/// A unique identifier for identifying publisher streams.
|
||||
var combineIdentifier: CombineIdentifier { get }
|
||||
}
|
||||
|
||||
|
||||
@@ -5,116 +5,208 @@
|
||||
// Created by Max Desiatov on 24/11/2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
/// A publisher that eventually produces one value and then finishes or fails.
|
||||
public final class Future<Output, Failure>: Publisher where Failure: Error {
|
||||
/// A publisher that eventually produces a single value and then finishes or fails.
|
||||
public final class Future<Output, Failure: Error>: Publisher {
|
||||
|
||||
/// A type that represents a closure to invoke in the future, when an element or error
|
||||
/// is available.
|
||||
///
|
||||
/// The promise closure receives one parameter: a `Result` that contains either
|
||||
/// a single element published by a `Future`, or an error.
|
||||
public typealias Promise = (Result<Output, Failure>) -> Void
|
||||
|
||||
private let _lock = UnfairRecursiveLock.allocate()
|
||||
private var _subscriptions: [Conduit] = []
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var downstreams = ConduitList<Output, Failure>.empty
|
||||
|
||||
private var result: Result<Output, Failure>?
|
||||
|
||||
/// Creates a publisher that invokes a promise closure when the publisher emits
|
||||
/// an element.
|
||||
///
|
||||
/// - Parameter attemptToFulfill: A `Promise` that the publisher invokes when
|
||||
/// the publisher emits an element or terminates with an error.
|
||||
public init(
|
||||
_ attemptToFulfill: @escaping (@escaping Promise) -> Void
|
||||
) {
|
||||
attemptToFulfill { result in
|
||||
self._lock.do {
|
||||
guard self.result == nil else { return }
|
||||
self.result = result
|
||||
self._publish(result)
|
||||
}
|
||||
}
|
||||
attemptToFulfill(self.promise)
|
||||
}
|
||||
|
||||
deinit {
|
||||
_lock.deallocate()
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
/// This function is called to attach the specified `Subscriber` to this
|
||||
/// `Publisher` by `subscribe(_:)`
|
||||
///
|
||||
/// - SeeAlso: `subscribe(_:)`
|
||||
/// - Parameters:
|
||||
/// - subscriber: The subscriber to attach to this `Publisher`.
|
||||
/// once attached it can begin to receive values.
|
||||
public func receive<Downstream: Subscriber>(
|
||||
subscriber: Downstream
|
||||
) where Output == Downstream.Input, Failure == Downstream.Failure {
|
||||
let subscription = Conduit(parent: self,
|
||||
downstream: AnySubscriber(subscriber))
|
||||
|
||||
_subscriptions.append(subscription)
|
||||
subscriber.receive(subscription: subscription)
|
||||
}
|
||||
|
||||
private func _acknowledgeDownstreamDemand() {
|
||||
_lock.do {
|
||||
guard let result = result else { return }
|
||||
_publish(result)
|
||||
private func promise(_ result: Result<Output, Failure>) {
|
||||
lock.lock()
|
||||
guard self.result == nil else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.result = result
|
||||
let downstreams = self.downstreams
|
||||
self.downstreams.removeAll()
|
||||
lock.unlock()
|
||||
switch result {
|
||||
case .success(let output):
|
||||
downstreams.forEach { $0.offer(output) }
|
||||
case .failure(let error):
|
||||
downstreams.forEach { $0.finish(completion: .failure(error)) }
|
||||
}
|
||||
}
|
||||
|
||||
private func _publish(_ result: Result<Output, Failure>) {
|
||||
for subscription in self._subscriptions where !subscription._isCompleted {
|
||||
switch result {
|
||||
case let .success(output) where subscription._demand > 0:
|
||||
subscription._demand -= 1
|
||||
subscription._demand += subscription._downstream?.receive(output) ?? .none
|
||||
subscription._receive(completion: .finished)
|
||||
case let .failure(error):
|
||||
subscription._receive(completion: .failure(error))
|
||||
|
||||
// nothing to do if no demand
|
||||
default: ()
|
||||
}
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Output == Downstream.Input, Failure == Downstream.Failure
|
||||
{
|
||||
let conduit = Conduit(parent: self, downstream: subscriber)
|
||||
lock.lock()
|
||||
if let result = self.result {
|
||||
downstreams.insert(conduit)
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: conduit)
|
||||
conduit.fulfill(result)
|
||||
} else {
|
||||
downstreams.insert(conduit)
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: conduit)
|
||||
}
|
||||
}
|
||||
|
||||
private func disassociate(_ conduit: ConduitBase<Output, Failure>) {
|
||||
lock.lock()
|
||||
downstreams.remove(conduit)
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
extension Future {
|
||||
|
||||
fileprivate final class Conduit: Subscription {
|
||||
private final class Conduit<Downstream: Subscriber>
|
||||
: ConduitBase<Output, Failure>,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
|
||||
fileprivate var _parent: Future<Output, Failure>?
|
||||
fileprivate var parent: Future?
|
||||
|
||||
fileprivate var _downstream: AnySubscriber<Output, Failure>?
|
||||
fileprivate var downstream: Downstream?
|
||||
|
||||
fileprivate var _demand: Subscribers.Demand = .none
|
||||
fileprivate var hasAnyDemand = false
|
||||
|
||||
fileprivate var _isCompleted: Bool {
|
||||
return _parent == nil
|
||||
private var lock = UnfairLock.allocate()
|
||||
|
||||
private var downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
fileprivate init(parent: Future, downstream: Downstream) {
|
||||
self.parent = parent
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
fileprivate init(parent: Future<Output, Failure>,
|
||||
downstream: AnySubscriber<Output, Failure>) {
|
||||
_parent = parent
|
||||
_downstream = downstream
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
fileprivate func _receive(completion: Subscribers.Completion<Failure>) {
|
||||
if !_isCompleted {
|
||||
_parent = nil
|
||||
_downstream?.receive(completion: completion)
|
||||
fileprivate func fulfill(_ result: Result<Output, Failure>) {
|
||||
lock.lock()
|
||||
guard let downstream = self.downstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
let parent = self.parent
|
||||
if case .success = result, !hasAnyDemand {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.downstream = nil
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
switch result {
|
||||
case .success(let output):
|
||||
_ = downstream.receive(output)
|
||||
downstream.receive(completion: .finished)
|
||||
case .failure(let error):
|
||||
downstream.receive(completion: .failure(error))
|
||||
}
|
||||
downstreamLock.unlock()
|
||||
parent?.disassociate(self)
|
||||
}
|
||||
|
||||
override func offer(_ output: Output) {
|
||||
fulfill(.success(output))
|
||||
}
|
||||
|
||||
override func finish(completion: Subscribers.Completion<Failure>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
assertionFailure("unreachable")
|
||||
case .failure(let error):
|
||||
fulfill(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func request(_ demand: Subscribers.Demand) {
|
||||
override func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
_parent?._lock.do {
|
||||
_demand += demand
|
||||
lock.lock()
|
||||
guard let downstream = self.downstream, let parent = self.parent else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
_parent?._acknowledgeDownstreamDemand()
|
||||
hasAnyDemand = true
|
||||
|
||||
parent.lock.lock()
|
||||
guard let result = parent.result else {
|
||||
parent.lock.unlock()
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
parent.lock.unlock()
|
||||
self.downstream = nil
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
switch result {
|
||||
case .success(let output):
|
||||
_ = downstream.receive(output)
|
||||
downstream.receive(completion: .finished)
|
||||
case .failure(let error):
|
||||
// This branch is not reachable under normal circumstances,
|
||||
// but may be reachable in case of a race condition.
|
||||
downstream.receive(completion: .failure(error))
|
||||
}
|
||||
downstreamLock.unlock()
|
||||
parent.disassociate(self)
|
||||
}
|
||||
|
||||
fileprivate func cancel() {
|
||||
_parent = nil
|
||||
override func cancel() {
|
||||
lock.lock()
|
||||
if self.downstream == nil {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.downstream = nil
|
||||
let parent = self.parent
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
parent?.disassociate(self)
|
||||
}
|
||||
|
||||
var description: String { return "Future" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("parent", parent as Any),
|
||||
("downstream", downstream as Any),
|
||||
("hasAnyDemand", hasAnyDemand),
|
||||
("subject", parent as Any)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
extension Future.Conduit: CustomStringConvertible {
|
||||
fileprivate var description: String { return "Future" }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// AbstractCombineLatest.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
internal class AbstractCombineLatest<Output, Failure, Downstream: Subscriber>
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
private let downstream: Downstream
|
||||
|
||||
// TODO: The size of these arrays always stays the same.
|
||||
// Maybe we can leverage ManagedBuffer/ManagedBufferPointer here
|
||||
// to avoid additional allocations.
|
||||
private var buffers: [Any?] // 0x78
|
||||
private var subscriptions: [Subscription?] // 0x80
|
||||
|
||||
private var demand = Subscribers.Demand.none // 0x88
|
||||
|
||||
private var recursion = false // 0x90
|
||||
|
||||
private var finished = false // 0x98
|
||||
|
||||
private var errored = false // 0xA0
|
||||
|
||||
private var cancelled = false // 0xA8
|
||||
|
||||
private let upstreamCount: Int // 0xB0
|
||||
|
||||
private var finishCount = 0 // 0xB8
|
||||
|
||||
private let lock = UnfairLock.allocate() // 0xC0
|
||||
|
||||
private let downstreamLock = UnfairRecursiveLock.allocate() // 0xC8
|
||||
|
||||
internal init(downstream: Downstream, upstreamCount: Int) {
|
||||
self.downstream = downstream
|
||||
self.buffers = Array(repeating: nil, count: upstreamCount)
|
||||
self.subscriptions = Array(repeating: nil, count: upstreamCount)
|
||||
self.upstreamCount = upstreamCount
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
// TODO: There should be more type-safe (and faster) way.
|
||||
// E. g. what if we store `buffers` in subclasses?
|
||||
internal func convert(values: [Any?]) -> Output {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
fileprivate final func receive(subscription: Subscription, index: Int) {
|
||||
lock.lock()
|
||||
guard !cancelled && subscriptions[index] == nil else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
subscriptions[index] = subscription
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
fileprivate final func receive(_ input: Any, index: Int) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
if cancelled || finished {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
buffers[index] = input
|
||||
guard !recursion && demand > 0 && buffers.allSatisfy({ $0 != nil }) else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
demand -= 1
|
||||
recursion = true
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(convert(values: buffers))
|
||||
downstreamLock.unlock()
|
||||
lock.lock()
|
||||
recursion = false
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
|
||||
fileprivate final func receive(completion: Subscribers.Completion<Failure>,
|
||||
index: Int) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
lock.lock()
|
||||
if finished {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
finishCount += 1
|
||||
subscriptions[index] = nil
|
||||
if finishCount == upstreamCount {
|
||||
finished = true
|
||||
buffers = Array(repeating: nil, count: upstreamCount)
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
case .failure:
|
||||
lock.lock()
|
||||
finished = true
|
||||
errored = true
|
||||
let subscriptions = self.subscriptions
|
||||
self.subscriptions = Array(repeating: nil, count: upstreamCount)
|
||||
buffers = Array(repeating: nil, count: upstreamCount)
|
||||
lock.unlock()
|
||||
for (i, subscription) in subscriptions.enumerated() where i != index {
|
||||
subscription?.cancel()
|
||||
}
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AbstractCombineLatest: Subscription {
|
||||
internal func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero() // TODO: Test this
|
||||
lock.lock()
|
||||
guard !cancelled && !finished else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
for subscription in subscriptions {
|
||||
subscription?.request(demand)
|
||||
}
|
||||
}
|
||||
|
||||
internal func cancel() {
|
||||
lock.lock()
|
||||
cancelled = true
|
||||
let subscriptions = self.subscriptions
|
||||
self.subscriptions = Array(repeating: nil, count: upstreamCount)
|
||||
buffers = Array(repeating: nil, count: upstreamCount)
|
||||
lock.unlock()
|
||||
for subscription in subscriptions {
|
||||
subscription?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AbstractCombineLatest: CustomStringConvertible {
|
||||
internal var description: String { return "CombineLatest" }
|
||||
}
|
||||
|
||||
extension AbstractCombineLatest: CustomReflectable {
|
||||
internal var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("downstream", downstream),
|
||||
("upstreamSubscriptions", subscriptions),
|
||||
("demand", demand),
|
||||
("buffers", buffers)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
}
|
||||
|
||||
extension AbstractCombineLatest: CustomPlaygroundDisplayConvertible {
|
||||
internal final var playgroundDescription: Any { return description }
|
||||
}
|
||||
|
||||
extension AbstractCombineLatest {
|
||||
internal struct Side<Input>: Subscriber, CustomStringConvertible {
|
||||
private let index: Int
|
||||
private let combiner: AbstractCombineLatest
|
||||
|
||||
internal let combineIdentifier = CombineIdentifier()
|
||||
|
||||
internal init(index: Int, combiner: AbstractCombineLatest) {
|
||||
self.index = index
|
||||
self.combiner = combiner
|
||||
}
|
||||
|
||||
internal func receive(subscription: Subscription) {
|
||||
combiner.receive(subscription: subscription, index: index)
|
||||
}
|
||||
|
||||
internal func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return combiner.receive(input, index: index)
|
||||
}
|
||||
|
||||
internal func receive(completion: Subscribers.Completion<Failure>) {
|
||||
combiner.receive(completion: completion, index: index)
|
||||
}
|
||||
|
||||
internal var description: String { return "CombineLatest" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// ConduitBase.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 25.06.2020.
|
||||
//
|
||||
|
||||
internal class ConduitBase<Output, Failure: Error>: Subscription {
|
||||
|
||||
internal init() {}
|
||||
|
||||
internal func offer(_ output: Output) {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
internal func finish(completion: Subscribers.Completion<Failure>) {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
internal func request(_ demand: Subscribers.Demand) {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
internal func cancel() {
|
||||
abstractMethod()
|
||||
}
|
||||
}
|
||||
|
||||
extension ConduitBase: Equatable {
|
||||
internal static func == (lhs: ConduitBase<Output, Failure>,
|
||||
rhs: ConduitBase<Output, Failure>) -> Bool {
|
||||
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
extension ConduitBase: Hashable {
|
||||
internal func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(ObjectIdentifier(self))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// ConduitList.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 25.06.2020.
|
||||
//
|
||||
|
||||
internal enum ConduitList<Output, Failure: Error> {
|
||||
case empty
|
||||
case single(ConduitBase<Output, Failure>)
|
||||
case many(Set<ConduitBase<Output, Failure>>)
|
||||
}
|
||||
|
||||
extension ConduitList {
|
||||
internal mutating func insert(_ conduit: ConduitBase<Output, Failure>) {
|
||||
switch self {
|
||||
case .empty:
|
||||
self = .single(conduit)
|
||||
case .single(conduit):
|
||||
break // This element already exists.
|
||||
case .single(let existingConduit):
|
||||
self = .many([existingConduit, conduit])
|
||||
case .many(var set):
|
||||
set.insert(conduit)
|
||||
self = .many(set)
|
||||
}
|
||||
}
|
||||
|
||||
internal func forEach(
|
||||
_ body: (ConduitBase<Output, Failure>) throws -> Void
|
||||
) rethrows {
|
||||
switch self {
|
||||
case .empty:
|
||||
break
|
||||
case .single(let conduit):
|
||||
try body(conduit)
|
||||
case .many(let set):
|
||||
try set.forEach(body)
|
||||
}
|
||||
}
|
||||
|
||||
internal mutating func remove(_ conduit: ConduitBase<Output, Failure>) {
|
||||
switch self {
|
||||
case .single(conduit):
|
||||
self = .empty
|
||||
case .empty, .single:
|
||||
break
|
||||
case .many(var set):
|
||||
set.remove(conduit)
|
||||
self = .many(set)
|
||||
}
|
||||
}
|
||||
|
||||
internal mutating func removeAll() {
|
||||
self = .empty
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// DebugHook.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 27.09.2020.
|
||||
//
|
||||
|
||||
internal final class DebugHook {
|
||||
|
||||
private struct Handler: Hashable {
|
||||
let handler: _Introspection
|
||||
|
||||
static func == (lhs: Handler, rhs: Handler) -> Bool {
|
||||
return lhs.handler === rhs.handler
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(ObjectIdentifier(handler))
|
||||
}
|
||||
}
|
||||
|
||||
internal static func getGlobalHook() -> DebugHook? {
|
||||
globalLock.lock()
|
||||
defer { globalLock.unlock() }
|
||||
return globalHook
|
||||
}
|
||||
|
||||
internal static func enable(_ handler: _Introspection) {
|
||||
let hook: DebugHook
|
||||
DebugHook.globalLock.lock()
|
||||
defer { DebugHook.globalLock.unlock() }
|
||||
if let _hook = DebugHook.globalHook {
|
||||
hook = _hook
|
||||
} else {
|
||||
hook = DebugHook()
|
||||
DebugHook.globalHook = hook
|
||||
}
|
||||
hook.lock.lock()
|
||||
defer { hook.lock.unlock() }
|
||||
hook.handlers.insert(Handler(handler: handler))
|
||||
}
|
||||
|
||||
internal static func disable(_ handler: _Introspection) {
|
||||
DebugHook.globalLock.lock()
|
||||
defer { DebugHook.globalLock.unlock() }
|
||||
guard let hook = DebugHook.globalHook else { return }
|
||||
hook.lock.lock()
|
||||
hook.handlers.remove(Handler(handler: handler))
|
||||
let noMoreHandlers = hook.handlers.isEmpty
|
||||
hook.lock.unlock()
|
||||
if noMoreHandlers {
|
||||
DebugHook.globalHook = nil
|
||||
}
|
||||
}
|
||||
|
||||
internal static func handlerIsEnabled(_ handler: _Introspection) -> Bool {
|
||||
DebugHook.globalLock.lock()
|
||||
defer { DebugHook.globalLock.unlock() }
|
||||
guard let hook = DebugHook.globalHook else { return false }
|
||||
hook.lock.lock()
|
||||
defer { hook.lock.unlock() }
|
||||
return hook.handlers.contains(Handler(handler: handler))
|
||||
}
|
||||
|
||||
private static var globalHook: DebugHook?
|
||||
|
||||
private static let globalLock = UnfairLock.allocate()
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var handlers = Set<Handler>()
|
||||
|
||||
internal var debugHandlers: [_Introspection] {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return handlers.map { $0.handler }
|
||||
}
|
||||
|
||||
private init() {}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
internal func willReceive<Upstream: Publisher, Downstream: Subscriber>(
|
||||
publisher: Upstream,
|
||||
subscriber: Downstream
|
||||
) where Upstream.Failure == Downstream.Failure, Upstream.Output == Downstream.Input {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.willReceive(publisher: publisher, subscriber: subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
internal func didReceive<Upstream: Publisher, Downstream: Subscriber>(
|
||||
publisher: Upstream,
|
||||
subscriber: Downstream
|
||||
) where Upstream.Failure == Downstream.Failure, Upstream.Output == Downstream.Input {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.didReceive(publisher: publisher, subscriber: subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
internal func willReceive<Downstream: Subscriber>(subscriber: Downstream,
|
||||
subscription: Subscription) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.willReceive(subscriber: subscriber, subscription: subscription)
|
||||
}
|
||||
}
|
||||
|
||||
internal func didReceive<Downstream: Subscriber>(subscriber: Downstream,
|
||||
subscription: Subscription) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.didReceive(subscriber: subscriber, subscription: subscription)
|
||||
}
|
||||
}
|
||||
|
||||
internal func willReceive<Downstream: Subscriber>(subscriber: Downstream,
|
||||
input: Downstream.Input) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.willReceive(subscriber: subscriber, input: input)
|
||||
}
|
||||
}
|
||||
|
||||
internal func didReceive<Downstream: Subscriber>(
|
||||
subscriber: Downstream,
|
||||
input: Downstream.Input,
|
||||
resultingDemand: Subscribers.Demand
|
||||
) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.didReceive(subscriber: subscriber,
|
||||
input: input,
|
||||
resultingDemand: resultingDemand)
|
||||
}
|
||||
}
|
||||
|
||||
internal func willReceive<Downstream: Subscriber>(
|
||||
subscriber: Downstream,
|
||||
completion: Subscribers.Completion<Downstream.Failure>
|
||||
) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.willReceive(subscriber: subscriber, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
internal func didReceive<Downstream: Subscriber>(
|
||||
subscriber: Downstream,
|
||||
completion: Subscribers.Completion<Downstream.Failure>
|
||||
) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.didReceive(subscriber: subscriber, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
internal func willRequest(subscription: Subscription, demand: Subscribers.Demand) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.willRequest(subscription: subscription, demand)
|
||||
}
|
||||
}
|
||||
|
||||
internal func didRequest(subscription: Subscription, demand: Subscribers.Demand) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.didRequest(subscription: subscription, demand)
|
||||
}
|
||||
}
|
||||
|
||||
internal func willCancel(subscription: Subscription) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.willCancel(subscription: subscription)
|
||||
}
|
||||
}
|
||||
|
||||
internal func didCancel(subscription: Subscription) {
|
||||
for debugHandler in debugHandlers {
|
||||
debugHandler.didCancel(subscription: subscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// EnumerateFields.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 31.10.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
internal typealias FieldEnumerator =
|
||||
(_ fieldName: UnsafePointer<CChar>, _ fieldOffset: Int, _ fieldType: Any.Type) -> Bool
|
||||
|
||||
internal func enumerateFields(ofType type: Any.Type,
|
||||
allowResilientSuperclasses: Bool,
|
||||
enumerator: FieldEnumerator) {
|
||||
// A neat trick to pass a Swift closure where a C function pointer is expected.
|
||||
// (Unlike closures, function pointers cannot capture context)
|
||||
withoutActuallyEscaping(enumerator) { enumerator in
|
||||
var context = enumerator
|
||||
enumerateFields(
|
||||
typeMetadata: unsafeBitCast(type, to: UnsafeRawPointer.self),
|
||||
allowResilientSuperclasses: allowResilientSuperclasses,
|
||||
enumeratorContext: &context,
|
||||
enumerator: { rawContext, fieldName, fieldOffset, rawMetadataPtr in
|
||||
rawContext
|
||||
.unsafelyUnwrapped
|
||||
.assumingMemoryBound(to: FieldEnumerator.self)
|
||||
.pointee(fieldName,
|
||||
fieldOffset,
|
||||
unsafeBitCast(rawMetadataPtr, to: Any.Type.self))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,11 @@
|
||||
// Created by Sergej Jaskiewicz on 23.10.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
/// A helper class that acts like both subscriber and subscription.
|
||||
///
|
||||
/// Filter-like operators send an instance of their `Inner` class that is subclass
|
||||
/// of this class to the upstream publisher (as subscriber) and
|
||||
/// to the downstream subcriber (as subscription).
|
||||
/// to the downstream subscriber (as subscription).
|
||||
///
|
||||
/// Filter-like operators include `Publishers.Filter`,
|
||||
/// `Publishers.RemoveDuplicates`, `Publishers.PrefixWhile` and more.
|
||||
@@ -26,8 +24,6 @@ internal class FilterProducer<Downstream: Subscriber,
|
||||
CustomReflectable
|
||||
where Downstream.Input == Output
|
||||
{
|
||||
// NOTE: This class has been audited for thread safety
|
||||
|
||||
// MARK: - State
|
||||
|
||||
private enum State {
|
||||
|
||||
@@ -5,14 +5,26 @@
|
||||
// Created by Sergej Jaskiewicz on 11.06.2019.
|
||||
//
|
||||
|
||||
#if canImport(COpenCombineHelpers)
|
||||
import COpenCombineHelpers
|
||||
#endif
|
||||
|
||||
extension UnfairRecursiveLock {
|
||||
|
||||
@inlinable
|
||||
internal func `do`<Result>(_ body: () throws -> Result) rethrows -> Result {
|
||||
lock()
|
||||
defer { unlock() }
|
||||
return try body()
|
||||
}
|
||||
#if WASI
|
||||
internal struct __UnfairLock { // swiftlint:disable:this type_name
|
||||
internal static func allocate() -> UnfairLock { return .init() }
|
||||
internal func lock() {}
|
||||
internal func unlock() {}
|
||||
internal func assertOwner() {}
|
||||
internal func deallocate() {}
|
||||
}
|
||||
|
||||
internal struct __UnfairRecursiveLock { // swiftlint:disable:this type_name
|
||||
internal static func allocate() -> UnfairRecursiveLock { return .init() }
|
||||
internal func lock() {}
|
||||
internal func unlock() {}
|
||||
internal func deallocate() {}
|
||||
}
|
||||
#endif // WASI
|
||||
|
||||
internal typealias UnfairLock = __UnfairLock
|
||||
internal typealias UnfairRecursiveLock = __UnfairRecursiveLock
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// PublishedSubject.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 29.10.2020.
|
||||
//
|
||||
|
||||
internal final class PublishedSubject<Output>: Subject {
|
||||
|
||||
internal typealias Failure = Never
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var downstreams = ConduitList<Output, Failure>.empty
|
||||
|
||||
private var currentValue: Output
|
||||
|
||||
private var upstreamSubscriptions: [Subscription] = []
|
||||
|
||||
private var hasAnyDownstreamDemand = false
|
||||
|
||||
private var changePublisher: ObservableObjectPublisher?
|
||||
|
||||
internal var value: Output {
|
||||
get {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return currentValue
|
||||
}
|
||||
set {
|
||||
send(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
internal var objectWillChange: ObservableObjectPublisher? {
|
||||
get {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return changePublisher
|
||||
}
|
||||
set {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
changePublisher = newValue
|
||||
}
|
||||
}
|
||||
|
||||
internal init(_ value: Output) {
|
||||
self.currentValue = value
|
||||
}
|
||||
|
||||
deinit {
|
||||
for subscription in upstreamSubscriptions {
|
||||
subscription.cancel()
|
||||
}
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
internal func send(subscription: Subscription) {
|
||||
lock.lock()
|
||||
upstreamSubscriptions.append(subscription)
|
||||
lock.unlock()
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
|
||||
internal func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Output, Downstream.Failure == Never
|
||||
{
|
||||
lock.lock()
|
||||
let conduit = Conduit(parent: self, downstream: subscriber)
|
||||
downstreams.insert(conduit)
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: conduit)
|
||||
}
|
||||
|
||||
internal func send(_ input: Output) {
|
||||
lock.lock()
|
||||
let downstreams = self.downstreams
|
||||
let changePublisher = self.changePublisher
|
||||
lock.unlock()
|
||||
changePublisher?.send()
|
||||
downstreams.forEach { conduit in
|
||||
conduit.offer(input)
|
||||
}
|
||||
lock.lock()
|
||||
currentValue = input
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
internal func send(completion: Subscribers.Completion<Never>) {
|
||||
fatalError("unreachable")
|
||||
}
|
||||
|
||||
private func disassociate(_ conduit: ConduitBase<Output, Failure>) {
|
||||
lock.lock()
|
||||
downstreams.remove(conduit)
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
extension PublishedSubject {
|
||||
|
||||
private final class Conduit<Downstream: Subscriber>
|
||||
: ConduitBase<Output, Failure>,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Output, Downstream.Failure == Never
|
||||
{
|
||||
|
||||
fileprivate var parent: PublishedSubject?
|
||||
|
||||
fileprivate var downstream: Downstream?
|
||||
|
||||
fileprivate var demand = Subscribers.Demand.none
|
||||
|
||||
private var lock = UnfairLock.allocate()
|
||||
|
||||
private var downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
private var deliveredCurrentValue = false
|
||||
|
||||
fileprivate init(parent: PublishedSubject,
|
||||
downstream: Downstream) {
|
||||
self.parent = parent
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
override func offer(_ output: Output) {
|
||||
lock.lock()
|
||||
guard demand > 0, let downstream = self.downstream else {
|
||||
deliveredCurrentValue = false
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
demand -= 1
|
||||
deliveredCurrentValue = true
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(output)
|
||||
downstreamLock.unlock()
|
||||
guard newDemand > 0 else { return }
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
override func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
guard let downstream = self.downstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
if deliveredCurrentValue {
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Hasn't yet delivered the current value
|
||||
|
||||
self.demand += demand
|
||||
deliveredCurrentValue = true
|
||||
if let currentValue = self.parent?.value {
|
||||
self.demand -= 1
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(currentValue)
|
||||
downstreamLock.unlock()
|
||||
guard newDemand > 0 else { return }
|
||||
lock.lock()
|
||||
self.demand += newDemand
|
||||
}
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
override func cancel() {
|
||||
lock.lock()
|
||||
if self.downstream == nil {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.downstream = nil
|
||||
let parent = self.parent
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
parent?.disassociate(self)
|
||||
}
|
||||
|
||||
var description: String { return "PublishedSubject" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("parent", parent as Any),
|
||||
("downstream", downstream as Any),
|
||||
("demand", demand),
|
||||
("subject", parent as Any)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// PublishedSubscriber.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 29.10.2020.
|
||||
//
|
||||
|
||||
internal struct PublishedSubscriber<Value>: Subscriber {
|
||||
|
||||
internal typealias Input = Value
|
||||
|
||||
internal typealias Failure = Never
|
||||
|
||||
internal let combineIdentifier = CombineIdentifier()
|
||||
|
||||
private weak var subject: PublishedSubject<Value>?
|
||||
|
||||
internal init(_ subject: PublishedSubject<Value>) {
|
||||
self.subject = subject
|
||||
}
|
||||
|
||||
internal func receive(subscription: Subscription) {
|
||||
subject?.send(subscription: subscription)
|
||||
}
|
||||
|
||||
internal func receive(_ input: Value) -> Subscribers.Demand {
|
||||
subject?.send(input)
|
||||
return .none
|
||||
}
|
||||
|
||||
internal func receive(completion: Subscribers.Completion<Never>) {}
|
||||
}
|
||||
@@ -5,13 +5,11 @@
|
||||
// Created by Sergej Jaskiewicz on 22.09.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
/// A helper class that acts like both subscriber and subscription.
|
||||
///
|
||||
/// Reduce-like operators send an instance of their `Inner` class that is subclass
|
||||
/// of this class to the upstream publisher (as subscriber) and
|
||||
/// to the downstream subcriber (as subsription).
|
||||
/// to the downstream subscriber (as subscription).
|
||||
///
|
||||
/// Reduce-like operators include `Publishers.Reduce`, `Publishers.TryReduce`,
|
||||
/// `Publishers.Count`, `Publishers.FirstWhere`, `Publishers.AllSatisfy` and more.
|
||||
@@ -50,8 +48,6 @@ internal class ReduceProducer<Downstream: Subscriber,
|
||||
|
||||
private var upstreamCompleted = false
|
||||
|
||||
private var empty = true
|
||||
|
||||
internal init(downstream: Downstream, initial: Output?, reduce: Reducer) {
|
||||
self.downstream = downstream
|
||||
self.initial = initial
|
||||
@@ -102,7 +98,9 @@ internal class ReduceProducer<Downstream: Subscriber,
|
||||
return
|
||||
}
|
||||
upstreamCompleted = true
|
||||
self.completed = downstreamRequested || empty
|
||||
if downstreamRequested {
|
||||
self.completed = true
|
||||
}
|
||||
let completed = self.completed
|
||||
let result = self.result
|
||||
lock.unlock()
|
||||
@@ -159,7 +157,6 @@ extension ReduceProducer: Subscriber {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
empty = false
|
||||
lock.unlock()
|
||||
|
||||
// Combine doesn't hold the lock when calling `receive(newValue:)`.
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
// Created by Sergej Jaskiewicz on 16/09/2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
// NOTE: This class has been audited for thread safety.
|
||||
internal final class SubjectSubscriber<Downstream: Subject>
|
||||
: Subscriber,
|
||||
CustomStringConvertible,
|
||||
@@ -16,7 +13,7 @@ internal final class SubjectSubscriber<Downstream: Subject>
|
||||
Subscription
|
||||
{
|
||||
private let lock = UnfairLock.allocate()
|
||||
private var downstreamSubject: Downstream?
|
||||
private weak var downstreamSubject: Downstream?
|
||||
private var upstreamSubscription: Subscription?
|
||||
|
||||
private var isCancelled: Bool { return downstreamSubject == nil }
|
||||
@@ -42,23 +39,21 @@ internal final class SubjectSubscriber<Downstream: Subject>
|
||||
|
||||
internal func receive(_ input: Downstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
guard let downstreamSubject = downstreamSubject else {
|
||||
guard let subject = downstreamSubject, upstreamSubscription != nil else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
guard upstreamSubscription != nil else { APIViolationValueBeforeSubscription() }
|
||||
lock.unlock()
|
||||
downstreamSubject.send(input)
|
||||
subject.send(input)
|
||||
return .none
|
||||
}
|
||||
|
||||
internal func receive(completion: Subscribers.Completion<Downstream.Failure>) {
|
||||
lock.lock()
|
||||
guard let subject = downstreamSubject else {
|
||||
guard let subject = downstreamSubject, upstreamSubscription != nil else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
guard upstreamSubscription != nil else { APIViolationUnexpectedCompletion() }
|
||||
lock.unlock()
|
||||
subject.send(completion: completion)
|
||||
downstreamSubject = nil
|
||||
@@ -89,11 +84,7 @@ internal final class SubjectSubscriber<Downstream: Subject>
|
||||
|
||||
internal func cancel() {
|
||||
lock.lock()
|
||||
if isCancelled {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
guard let subscription = upstreamSubscription else {
|
||||
guard !isCancelled, let subscription = upstreamSubscription else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// SubscriberTap.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 27.09.2020.
|
||||
//
|
||||
|
||||
internal protocol SubscriberTapMarker {
|
||||
var inner: Any { mutating get }
|
||||
}
|
||||
|
||||
internal struct SubscriberTap<Subscriber: OpenCombine.Subscriber>
|
||||
: OpenCombine.Subscriber,
|
||||
CustomStringConvertible,
|
||||
SubscriberTapMarker
|
||||
{
|
||||
internal typealias Input = Subscriber.Input
|
||||
|
||||
internal typealias Failure = Subscriber.Failure
|
||||
|
||||
private let subscriber: Subscriber
|
||||
|
||||
internal lazy var inner: Any = AnySubscriber(self.subscriber)
|
||||
|
||||
internal init(subscriber: Subscriber) {
|
||||
self.subscriber = subscriber
|
||||
}
|
||||
|
||||
internal var combineIdentifier: CombineIdentifier {
|
||||
return subscriber.combineIdentifier
|
||||
}
|
||||
|
||||
internal func receive(subscription: Subscription) {
|
||||
let hook = DebugHook.getGlobalHook()
|
||||
if let subscriptionTap = subscription as? SubscriptionTap {
|
||||
hook?.willReceive(subscriber: subscriber,
|
||||
subscription: subscriptionTap.subscription)
|
||||
subscriber.receive(subscription: subscriptionTap)
|
||||
hook?.didReceive(subscriber: subscriber,
|
||||
subscription: subscriptionTap.subscription)
|
||||
} else {
|
||||
hook?.willReceive(subscriber: subscriber, subscription: subscription)
|
||||
subscriber
|
||||
.receive(subscription: SubscriptionTap(subscription: subscription))
|
||||
hook?.didReceive(subscriber: subscriber, subscription: subscription)
|
||||
}
|
||||
}
|
||||
|
||||
internal func receive(_ input: Input) -> Subscribers.Demand {
|
||||
let hook = DebugHook.getGlobalHook()
|
||||
hook?.willReceive(subscriber: subscriber, input: input)
|
||||
let newDemand = subscriber.receive(input)
|
||||
hook?.didReceive(subscriber: subscriber,
|
||||
input: input,
|
||||
resultingDemand: newDemand)
|
||||
return newDemand
|
||||
}
|
||||
|
||||
internal func receive(completion: Subscribers.Completion<Subscriber.Failure>) {
|
||||
let hook = DebugHook.getGlobalHook()
|
||||
hook?.willReceive(subscriber: subscriber, completion: completion)
|
||||
subscriber.receive(completion: completion)
|
||||
hook?.didReceive(subscriber: subscriber, completion: completion)
|
||||
}
|
||||
|
||||
internal var description: String {
|
||||
return String(describing: subscriber)
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,26 @@
|
||||
internal enum SubscriptionStatus {
|
||||
case awaitingSubscription
|
||||
case subscribed(Subscription)
|
||||
case pendingTerminal(Subscription)
|
||||
case terminal
|
||||
}
|
||||
|
||||
extension SubscriptionStatus {
|
||||
internal var isAwaitingSubscription: Bool {
|
||||
switch self {
|
||||
case .awaitingSubscription:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
internal var subscription: Subscription? {
|
||||
switch self {
|
||||
case .awaitingSubscription, .terminal:
|
||||
return nil
|
||||
case let .subscribed(subscription), let .pendingTerminal(subscription):
|
||||
return subscription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// SubscriptionTap.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 27.09.2020.
|
||||
//
|
||||
|
||||
internal struct SubscriptionTap: Subscription, CustomStringConvertible {
|
||||
|
||||
internal let subscription: Subscription
|
||||
|
||||
internal var combineIdentifier: CombineIdentifier {
|
||||
return subscription.combineIdentifier
|
||||
}
|
||||
|
||||
internal func request(_ demand: Subscribers.Demand) {
|
||||
let hook = DebugHook.getGlobalHook()
|
||||
hook?.willRequest(subscription: subscription, demand: demand)
|
||||
subscription.request(demand)
|
||||
hook?.didRequest(subscription: subscription, demand: demand)
|
||||
}
|
||||
|
||||
internal func cancel() {
|
||||
let hook = DebugHook.getGlobalHook()
|
||||
hook?.willCancel(subscription: subscription)
|
||||
subscription.cancel()
|
||||
hook?.didCancel(subscription: subscription)
|
||||
}
|
||||
|
||||
internal var description: String {
|
||||
return String(describing: subscription)
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,9 @@
|
||||
|
||||
/// A scheduler for performing synchronous actions.
|
||||
///
|
||||
/// You can use this scheduler for immediate actions. If you attempt to schedule
|
||||
/// actions after a specific date, the scheduler produces a fatal error.
|
||||
/// You can only use this scheduler for immediate actions. If you attempt to schedule
|
||||
/// actions after a specific date, this scheduler ignores the date and performs them
|
||||
/// immediately.
|
||||
public struct ImmediateScheduler: Scheduler {
|
||||
|
||||
/// The time type used by the immediate scheduler.
|
||||
@@ -40,29 +41,42 @@ public struct ImmediateScheduler: Scheduler {
|
||||
Codable,
|
||||
SchedulerTimeIntervalConvertible {
|
||||
|
||||
/// The type used when evaluating floating-point literals.
|
||||
public typealias FloatLiteralType = Double
|
||||
|
||||
/// The type used when evaluating integer literals.
|
||||
public typealias IntegerLiteralType = Int
|
||||
|
||||
/// The type used for expressing the stride’s magnitude.
|
||||
public typealias Magnitude = Int
|
||||
|
||||
/// The value of this time interval in seconds.
|
||||
public var magnitude: Int
|
||||
|
||||
/// Creates an immediate scheduler time interval from the given time interval.
|
||||
@inlinable
|
||||
public init(_ value: Int) {
|
||||
magnitude = value
|
||||
}
|
||||
|
||||
/// Creates an immediate scheduler time interval from an integer seconds
|
||||
/// value.
|
||||
@inlinable
|
||||
public init(integerLiteral value: Int) {
|
||||
self.init(value)
|
||||
}
|
||||
|
||||
/// Creates an immediate scheduler time interval from a floating-point seconds
|
||||
/// value.
|
||||
@inlinable
|
||||
public init(floatLiteral value: Double) {
|
||||
self.init(Int(value))
|
||||
}
|
||||
|
||||
/// Creates an immediate scheduler time interval from a binary integer type.
|
||||
///
|
||||
/// If `exactly` can’t convert to an `Int`, the resulting time interval is
|
||||
/// `nil`.
|
||||
@inlinable
|
||||
public init?<BinaryIntegerType: BinaryInteger>(
|
||||
exactly source: BinaryIntegerType
|
||||
@@ -118,6 +132,7 @@ public struct ImmediateScheduler: Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that defines options accepted by the immediate scheduler.
|
||||
public typealias SchedulerOptions = Never
|
||||
|
||||
/// The shared instance of the immediate scheduler.
|
||||
@@ -126,15 +141,21 @@ public struct ImmediateScheduler: Scheduler {
|
||||
/// the shared instance.
|
||||
public static let shared = ImmediateScheduler()
|
||||
|
||||
/// Performs the action at the next possible opportunity.
|
||||
@inlinable
|
||||
public func schedule(options: SchedulerOptions?, _ action: @escaping () -> Void) {
|
||||
action()
|
||||
}
|
||||
|
||||
/// The immediate scheduler’s definition of the current moment in time.
|
||||
public var now: SchedulerTimeType { return SchedulerTimeType() }
|
||||
|
||||
/// The minimum tolerance allowed by the immediate scheduler.
|
||||
public var minimumTolerance: SchedulerTimeType.Stride { return 0 }
|
||||
|
||||
/// Performs the action at some time after the specified date.
|
||||
///
|
||||
/// The immediate scheduler ignores `date` and performs the action immediately.
|
||||
public func schedule(after date: SchedulerTimeType,
|
||||
tolerance: SchedulerTimeType.Stride,
|
||||
options: SchedulerOptions?,
|
||||
@@ -144,6 +165,8 @@ public struct ImmediateScheduler: Scheduler {
|
||||
|
||||
/// Performs the action at some time after the specified date, at the specified
|
||||
/// frequency, optionally taking into account tolerance if possible.
|
||||
///
|
||||
/// The immediate scheduler ignores `date` and performs the action immediately.
|
||||
public func schedule(after date: SchedulerTimeType,
|
||||
interval: SchedulerTimeType.Stride,
|
||||
tolerance: SchedulerTimeType.Stride,
|
||||
|
||||
@@ -5,65 +5,10 @@
|
||||
// Created by Sergej Jaskiewicz on 08/09/2019.
|
||||
//
|
||||
|
||||
// We use type metadata in the implementation of ObservableObject,
|
||||
// but type metadata is stable only on Darwin. There are no such guarantees
|
||||
// on non-Apple platforms (yet).
|
||||
//
|
||||
// This means that on Linux the layout of type metadata can change in a new Swift release,
|
||||
// which will cause bugs that are hard to track (basically, undefined behavior).
|
||||
//
|
||||
// Whenever a new Swift version is available, we well test OpenCombine against it,
|
||||
// and if everything works, release an update as soon as possible where the maximum
|
||||
// supported Swift version is incremented.
|
||||
#if !canImport(Darwin) && swift(>=5.1.50)
|
||||
#warning("""
|
||||
ObservableObject is not guaranteed to work on non-Apple platforms with this version \
|
||||
of Swift because its implementation relies on ABI stability.
|
||||
|
||||
In order to fix this warning, please update to the newest version of OpenCombine, \
|
||||
or create an issue at https://github.com/broadwaylamb/OpenCombine if there is no \
|
||||
newer version yet.
|
||||
""")
|
||||
#endif
|
||||
|
||||
#if swift(>=5.1)
|
||||
private protocol _ObservableObjectProperty {
|
||||
var objectWillChange: ObservableObjectPublisher? { get set }
|
||||
}
|
||||
|
||||
extension _ObservableObjectProperty {
|
||||
|
||||
fileprivate static func installPublisher(
|
||||
_ publisher: ObservableObjectPublisher,
|
||||
on publishedStorage: UnsafeMutableRawPointer
|
||||
) {
|
||||
// It is safe to call assumingMemoryBound here because we know for sure
|
||||
// that the actual type of the pointee is Self.
|
||||
publishedStorage
|
||||
.assumingMemoryBound(to: Self.self)
|
||||
.pointee
|
||||
.objectWillChange = publisher
|
||||
}
|
||||
|
||||
fileprivate static func getPublisher(
|
||||
from publishedStorage: UnsafeMutableRawPointer
|
||||
) -> ObservableObjectPublisher? {
|
||||
// It is safe to call assumingMemoryBound here because we know for sure
|
||||
// that the actual type of the pointee is Self.
|
||||
return publishedStorage
|
||||
.assumingMemoryBound(to: Self.self)
|
||||
.pointee
|
||||
.objectWillChange
|
||||
}
|
||||
}
|
||||
|
||||
extension Published: _ObservableObjectProperty {}
|
||||
#endif
|
||||
|
||||
/// A type of object with a publisher that emits before the object has changed.
|
||||
///
|
||||
/// By default an `ObservableObject` will synthesize an `objectWillChange`
|
||||
/// publisher that emits before any of its `@Published` properties changes:
|
||||
/// By default an `ObservableObject` synthesizes an `objectWillChange` publisher that
|
||||
/// emits the changed value before any of its `@Published` properties changes.
|
||||
///
|
||||
/// class Contact : ObservableObject {
|
||||
/// @Published var name: String
|
||||
@@ -80,11 +25,13 @@ extension Published: _ObservableObjectProperty {}
|
||||
/// }
|
||||
///
|
||||
/// let john = Contact(name: "John Appleseed", age: 24)
|
||||
/// john.objectWillChange.sink { _ in print("will change") }
|
||||
/// print(john.haveBirthday)
|
||||
/// // Prints "will change"
|
||||
/// cancellable = john.objectWillChange
|
||||
/// .sink { _ in
|
||||
/// print("\(john.age) will change")
|
||||
/// }
|
||||
/// print(john.haveBirthday())
|
||||
/// // Prints "24 will change"
|
||||
/// // Prints "25"
|
||||
///
|
||||
public protocol ObservableObject: AnyObject {
|
||||
|
||||
/// The type of publisher that emits before the object has changed.
|
||||
@@ -95,83 +42,186 @@ public protocol ObservableObject: AnyObject {
|
||||
var objectWillChange: ObjectWillChangePublisher { get }
|
||||
}
|
||||
|
||||
private protocol _ObservableObjectProperty {
|
||||
var objectWillChange: ObservableObjectPublisher? { get nonmutating set }
|
||||
}
|
||||
|
||||
#if swift(>=5.1)
|
||||
extension Published: _ObservableObjectProperty {}
|
||||
|
||||
extension ObservableObject where ObjectWillChangePublisher == ObservableObjectPublisher {
|
||||
|
||||
/// A publisher that emits before the object has changed.
|
||||
public var objectWillChange: ObservableObjectPublisher {
|
||||
#if swift(>=5.1)
|
||||
var installedPublisher: ObservableObjectPublisher?
|
||||
var reflection: Mirror? = Mirror(reflecting: self)
|
||||
while let aClass = reflection {
|
||||
for (_, property) in aClass.children {
|
||||
guard let property = property as? _ObservableObjectProperty else {
|
||||
// Visit other fields until we meet a @Published field
|
||||
continue
|
||||
}
|
||||
|
||||
enumerateFields(ofType: Self.self,
|
||||
allowResilientSuperclasses: false) { _, fieldOffset, fieldType in
|
||||
let storage = Unmanaged
|
||||
.passUnretained(self)
|
||||
.toOpaque()
|
||||
.advanced(by: fieldOffset)
|
||||
// Now we know that the field is @Published.
|
||||
if let alreadyInstalledPublisher = property.objectWillChange {
|
||||
installedPublisher = alreadyInstalledPublisher
|
||||
// Don't visit other fields, as all @Published fields
|
||||
// already have a publisher installed.
|
||||
break
|
||||
}
|
||||
|
||||
guard let fieldType = fieldType as? _ObservableObjectProperty.Type else {
|
||||
// Visit other fields until we meet a @Published field
|
||||
return true
|
||||
}
|
||||
|
||||
// Now we know that the field is @Published.
|
||||
|
||||
if let alreadyInstalledPublisher = fieldType.getPublisher(from: storage) {
|
||||
installedPublisher = alreadyInstalledPublisher
|
||||
// Don't visit other fields, as all @Published fields
|
||||
// already have a publisher installed.
|
||||
return false
|
||||
}
|
||||
|
||||
// Okay, this field doesn't have a publisher installed.
|
||||
// This means that other fields don't have it either
|
||||
// (because we install it only once and fields can't be added at runtime).
|
||||
|
||||
var lazilyCreatedPublisher: ObjectWillChangePublisher {
|
||||
if let publisher = installedPublisher {
|
||||
// Okay, this field doesn't have a publisher installed.
|
||||
// This means that other fields don't have it either
|
||||
// (because we install it only once and fields can't be added at runtime).
|
||||
var lazilyCreatedPublisher: ObjectWillChangePublisher {
|
||||
if let publisher = installedPublisher {
|
||||
return publisher
|
||||
}
|
||||
let publisher = ObservableObjectPublisher()
|
||||
installedPublisher = publisher
|
||||
return publisher
|
||||
}
|
||||
let publisher = ObservableObjectPublisher()
|
||||
installedPublisher = publisher
|
||||
return publisher
|
||||
|
||||
property.objectWillChange = lazilyCreatedPublisher
|
||||
|
||||
// Continue visiting other fields.
|
||||
}
|
||||
|
||||
fieldType.installPublisher(lazilyCreatedPublisher, on: storage)
|
||||
|
||||
// Continue visiting other fields.
|
||||
return true
|
||||
reflection = aClass.superclassMirror
|
||||
}
|
||||
|
||||
return installedPublisher ?? ObservableObjectPublisher()
|
||||
#else
|
||||
// There are no @Published in Swift 5.0, so we act the same as in Swift 5.1
|
||||
// with classes without @Published properties.
|
||||
// We create a new instance every time.
|
||||
return ObservableObjectPublisher()
|
||||
#endif // swift(>=5.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// The default publisher of an `ObservableObject`.
|
||||
#endif
|
||||
|
||||
/// A publisher that publishes changes from observable objects.
|
||||
public final class ObservableObjectPublisher: Publisher {
|
||||
|
||||
public typealias Output = Void
|
||||
|
||||
public typealias Failure = Never
|
||||
|
||||
private let subject: PassthroughSubject<Void, Never>
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
public init() {
|
||||
subject = .init()
|
||||
private var connections = Set<Conduit>()
|
||||
|
||||
// TODO: Combine needs this for some reason
|
||||
private var identifier: ObjectIdentifier?
|
||||
|
||||
/// Creates an observable object publisher instance.
|
||||
public init() {}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Void, Downstream.Failure == Never
|
||||
{
|
||||
subject.subscribe(subscriber)
|
||||
let inner = Inner(downstream: subscriber, parent: self)
|
||||
lock.lock()
|
||||
connections.insert(inner)
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: inner)
|
||||
}
|
||||
|
||||
public func send() {
|
||||
subject.send()
|
||||
lock.lock()
|
||||
let connections = self.connections
|
||||
lock.unlock()
|
||||
for connection in connections {
|
||||
connection.send()
|
||||
}
|
||||
}
|
||||
|
||||
private func remove(_ conduit: Conduit) {
|
||||
lock.lock()
|
||||
connections.remove(conduit)
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
extension ObservableObjectPublisher {
|
||||
private class Conduit: Hashable {
|
||||
|
||||
fileprivate func send() {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
fileprivate static func == (lhs: Conduit, rhs: Conduit) -> Bool {
|
||||
return lhs === rhs
|
||||
}
|
||||
|
||||
fileprivate func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(ObjectIdentifier(self))
|
||||
}
|
||||
}
|
||||
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Conduit,
|
||||
Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Void, Downstream.Failure == Never
|
||||
{
|
||||
private enum State {
|
||||
case initialized
|
||||
case active
|
||||
case terminal
|
||||
}
|
||||
|
||||
private weak var parent: ObservableObjectPublisher?
|
||||
private let downstream: Downstream
|
||||
private let downstreamLock = UnfairRecursiveLock.allocate()
|
||||
private let lock = UnfairLock.allocate()
|
||||
private var state = State.initialized
|
||||
|
||||
init(downstream: Downstream, parent: ObservableObjectPublisher) {
|
||||
self.parent = parent
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
deinit {
|
||||
downstreamLock.deallocate()
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
override func send() {
|
||||
lock.lock()
|
||||
let state = self.state
|
||||
lock.unlock()
|
||||
if state == .active {
|
||||
downstreamLock.lock()
|
||||
_ = downstream.receive()
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
if state == .initialized {
|
||||
state = .active
|
||||
}
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
parent?.remove(self)
|
||||
}
|
||||
|
||||
var description: String { return "ObservableObjectPublisher" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
let children = CollectionOfOne<Mirror.Child>(("downstream", downstream))
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any {
|
||||
return description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,20 +5,24 @@
|
||||
// Created by Sergej Jaskiewicz on 11.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
/// A subject that passes along values and completion.
|
||||
/// A subject that broadcasts elements to downstream subscribers.
|
||||
///
|
||||
/// Use a `PassthroughSubject` in unit tests when you want a publisher than can publish
|
||||
/// specific values on-demand during tests.
|
||||
public final class PassthroughSubject<Output, Failure: Error>: Subject {
|
||||
/// As a concrete implementation of `Subject`, the `PassthroughSubject` provides
|
||||
/// a convenient way to adapt existing imperative code to the Combine model.
|
||||
///
|
||||
/// Unlike `CurrentValueSubject`, a `PassthroughSubject` doesn’t have an initial value or
|
||||
/// a buffer of the most recently-published element.
|
||||
/// A `PassthroughSubject` drops values if there are no subscribers, or its current demand
|
||||
/// is zero.
|
||||
public final class PassthroughSubject<Output, Failure: Error>: Subject {
|
||||
|
||||
private let _lock = UnfairRecursiveLock.allocate()
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var _completion: Subscribers.Completion<Failure>?
|
||||
private var active = true
|
||||
|
||||
// TODO: Combine uses bag data structure
|
||||
private var _subscriptions: [Conduit] = []
|
||||
private var completion: Subscribers.Completion<Failure>?
|
||||
|
||||
private var downstreams = ConduitList<Output, Failure>.empty
|
||||
|
||||
internal var upstreamSubscriptions: [Subscription] = []
|
||||
|
||||
@@ -27,112 +31,197 @@ public final class PassthroughSubject<Output, Failure: Error>: Subject {
|
||||
public init() {}
|
||||
|
||||
deinit {
|
||||
for subscription in _subscriptions {
|
||||
subscription._downstream = nil
|
||||
for subscription in upstreamSubscriptions {
|
||||
subscription.cancel()
|
||||
}
|
||||
_lock.deallocate()
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
public func send(subscription: Subscription) {
|
||||
_lock.do {
|
||||
upstreamSubscriptions.append(subscription)
|
||||
if hasAnyDownstreamDemand {
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
lock.lock()
|
||||
upstreamSubscriptions.append(subscription)
|
||||
let hasAnyDownstreamDemand = self.hasAnyDownstreamDemand
|
||||
lock.unlock()
|
||||
if hasAnyDownstreamDemand {
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Output == Downstream.Input, Failure == Downstream.Failure
|
||||
{
|
||||
_lock.do {
|
||||
if let completion = _completion {
|
||||
subscriber.receive(subscription: Subscriptions.empty)
|
||||
subscriber.receive(completion: completion)
|
||||
return
|
||||
} else {
|
||||
let subscription = Conduit(parent: self,
|
||||
downstream: AnySubscriber(subscriber))
|
||||
|
||||
_subscriptions.append(subscription)
|
||||
subscriber.receive(subscription: subscription)
|
||||
}
|
||||
lock.lock()
|
||||
if active {
|
||||
let conduit = Conduit(parent: self, downstream: subscriber)
|
||||
downstreams.insert(conduit)
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: conduit)
|
||||
} else {
|
||||
let completion = self.completion!
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: Subscriptions.empty)
|
||||
subscriber.receive(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
public func send(_ input: Output) {
|
||||
_lock.do {
|
||||
for subscription in _subscriptions
|
||||
where !subscription._isCompleted && subscription._demand > 0
|
||||
{
|
||||
let newDemand = subscription._downstream?.receive(input) ?? .none
|
||||
subscription._demand += newDemand
|
||||
subscription._demand -= 1
|
||||
}
|
||||
lock.lock()
|
||||
guard active else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
let downstreams = self.downstreams
|
||||
lock.unlock()
|
||||
downstreams.forEach { conduit in
|
||||
conduit.offer(input)
|
||||
}
|
||||
}
|
||||
|
||||
public func send(completion: Subscribers.Completion<Failure>) {
|
||||
_lock.do {
|
||||
_completion = completion
|
||||
for subscriber in _subscriptions {
|
||||
subscriber._receive(completion: completion)
|
||||
}
|
||||
lock.lock()
|
||||
guard active else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
active = false
|
||||
self.completion = completion
|
||||
let downstreams = self.downstreams
|
||||
self.downstreams.removeAll()
|
||||
lock.unlock()
|
||||
downstreams.forEach { conduit in
|
||||
conduit.finish(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
private func _acknowledgeDownstreamDemand() {
|
||||
_lock.do {
|
||||
guard !hasAnyDownstreamDemand else { return }
|
||||
hasAnyDownstreamDemand = true
|
||||
for subscription in upstreamSubscriptions {
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
private func acknowledgeDownstreamDemand() {
|
||||
lock.lock()
|
||||
if hasAnyDownstreamDemand {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
hasAnyDownstreamDemand = true
|
||||
let upstreamSubscriptions = self.upstreamSubscriptions
|
||||
lock.unlock()
|
||||
for subscription in upstreamSubscriptions {
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
}
|
||||
|
||||
private func disassociate(_ conduit: ConduitBase<Output, Failure>) {
|
||||
lock.lock()
|
||||
guard active else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
downstreams.remove(conduit)
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
extension PassthroughSubject {
|
||||
|
||||
fileprivate final class Conduit: Subscription {
|
||||
private final class Conduit<Downstream: Subscriber>
|
||||
: ConduitBase<Output, Failure>,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
|
||||
fileprivate var _parent: PassthroughSubject?
|
||||
fileprivate var parent: PassthroughSubject?
|
||||
|
||||
fileprivate var _downstream: AnySubscriber<Output, Failure>?
|
||||
fileprivate var downstream: Downstream?
|
||||
|
||||
fileprivate var _demand: Subscribers.Demand = .none
|
||||
fileprivate var demand = Subscribers.Demand.none
|
||||
|
||||
fileprivate var _isCompleted: Bool {
|
||||
return _parent == nil
|
||||
}
|
||||
private var lock = UnfairLock.allocate()
|
||||
|
||||
private var downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
fileprivate init(parent: PassthroughSubject,
|
||||
downstream: AnySubscriber<Output, Failure>) {
|
||||
_parent = parent
|
||||
_downstream = downstream
|
||||
downstream: Downstream) {
|
||||
self.parent = parent
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
fileprivate func _receive(completion: Subscribers.Completion<Failure>) {
|
||||
if !_isCompleted {
|
||||
_parent = nil
|
||||
_downstream?.receive(completion: completion)
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
override func offer(_ output: Output) {
|
||||
lock.lock()
|
||||
guard demand > 0, let downstream = self.downstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
demand -= 1
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(output)
|
||||
downstreamLock.unlock()
|
||||
guard newDemand > 0 else { return }
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
fileprivate func request(_ demand: Subscribers.Demand) {
|
||||
override func finish(completion: Subscribers.Completion<Failure>) {
|
||||
lock.lock()
|
||||
guard let downstream = self.downstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.downstream = nil
|
||||
let parent = self.parent
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
parent?.disassociate(self)
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
override func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
_parent?._lock.do {
|
||||
_demand += demand
|
||||
lock.lock()
|
||||
if self.downstream == nil {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
_parent?._acknowledgeDownstreamDemand()
|
||||
self.demand += demand
|
||||
let parent = self.parent
|
||||
lock.unlock()
|
||||
parent?.acknowledgeDownstreamDemand()
|
||||
}
|
||||
|
||||
fileprivate func cancel() {
|
||||
_parent = nil
|
||||
override func cancel() {
|
||||
lock.lock()
|
||||
if self.downstream == nil {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.downstream = nil
|
||||
let parent = self.parent
|
||||
self.parent = nil
|
||||
lock.unlock()
|
||||
parent?.disassociate(self)
|
||||
}
|
||||
|
||||
var description: String { return "PassthroughSubject" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("parent", parent as Any),
|
||||
("downstream", downstream as Any),
|
||||
("demand", demand),
|
||||
("subject", parent as Any)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
extension PassthroughSubject.Conduit: CustomStringConvertible {
|
||||
fileprivate var description: String { return "PassthroughSubject" }
|
||||
}
|
||||
|
||||
@@ -6,30 +6,83 @@
|
||||
//
|
||||
|
||||
#if swift(>=5.1)
|
||||
/// Adds a `Publisher` to a property.
|
||||
|
||||
extension Publisher where Failure == Never {
|
||||
|
||||
/// Republishes elements received from a publisher, by assigning them to a property
|
||||
/// marked as a publisher.
|
||||
///
|
||||
/// Use this operator when you want to receive elements from a publisher and republish
|
||||
/// them through a property marked with the `@Published` attribute. The `assign(to:)`
|
||||
/// operator manages the life cycle of the subscription, canceling the subscription
|
||||
/// automatically when the `Published` instance deinitializes. Because of this,
|
||||
/// the `assign(to:)` operator doesn't return an `AnyCancellable` that you're
|
||||
/// responsible for like `assign(to:on:)` does.
|
||||
///
|
||||
/// The example below shows a model class that receives elements from an internal
|
||||
/// `Timer.TimerPublisher`, and assigns them to a `@Published` property called
|
||||
/// `lastUpdated`:
|
||||
///
|
||||
/// class MyModel: ObservableObject {
|
||||
/// @Published var lastUpdated: Date = Date()
|
||||
/// init() {
|
||||
/// Timer.publish(every: 1.0, on: .main, in: .common)
|
||||
/// .autoconnect()
|
||||
/// .assign(to: $lastUpdated)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// If you instead implemented `MyModel` with `assign(to: lastUpdated, on: self)`,
|
||||
/// storing the returned `AnyCancellable` instance could cause a reference cycle,
|
||||
/// because the `Subscribers.Assign` subscriber would hold a strong reference
|
||||
/// to `self`. Using `assign(to:)` solves this problem.
|
||||
///
|
||||
/// - Parameter published: A property marked with the `@Published` attribute, which
|
||||
/// receives and republishes all elements received from the upstream publisher.
|
||||
public func assign(to published: inout Published<Output>.Publisher) {
|
||||
subscribe(PublishedSubscriber(published.subject))
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that publishes a property marked with an attribute.
|
||||
///
|
||||
/// Properties annotated with `@Published` contain both the stored value
|
||||
/// and a publisher which sends any new values after the property value
|
||||
/// has been sent. New subscribers will receive the current value
|
||||
/// of the property first.
|
||||
/// Note that the `@Published` property is class-constrained.
|
||||
/// Use it with properties of classes, not with non-class types like structures.
|
||||
/// Publishing a property with the `@Published` attribute creates a publisher of this
|
||||
/// type. You access the publisher with the `$` operator, as shown here:
|
||||
///
|
||||
/// class Weather {
|
||||
/// @Published var temperature: Double
|
||||
/// init(temperature: Double) {
|
||||
/// self.temperature = temperature
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let weather = Weather(temperature: 20)
|
||||
/// cancellable = weather.$temperature
|
||||
/// .sink() {
|
||||
/// print ("Temperature now: \($0)")
|
||||
/// }
|
||||
/// weather.temperature = 25
|
||||
///
|
||||
/// // Prints:
|
||||
/// // Temperature now: 20.0
|
||||
/// // Temperature now: 25.0
|
||||
///
|
||||
/// When the property changes, publishing occurs in the property's `willSet` block,
|
||||
/// meaning subscribers receive the new value before it's actually set on the property.
|
||||
/// In the above example, the second time the sink executes its closure, it receives
|
||||
/// the parameter value `25`. However, if the closure evaluated `weather.temperature`,
|
||||
/// the value returned would be `20`.
|
||||
///
|
||||
/// > Important: The `@Published` attribute is class constrained. Use it with properties
|
||||
/// of classes, not with non-class types like structures.
|
||||
///
|
||||
/// ### See Also
|
||||
///
|
||||
/// - `Publisher.assign(to:)`
|
||||
@available(swift, introduced: 5.1)
|
||||
@propertyWrapper
|
||||
public struct Published<Value> {
|
||||
|
||||
/// Initialize the storage of the `Published` property as well as the corresponding
|
||||
/// `Publisher`.
|
||||
public init(initialValue: Value) {
|
||||
self.init(wrappedValue: initialValue)
|
||||
}
|
||||
|
||||
/// Initialize the storage of the `Published` property as well as the corresponding
|
||||
/// `Publisher`.
|
||||
public init(wrappedValue: Value) {
|
||||
value = wrappedValue
|
||||
}
|
||||
|
||||
/// A publisher for properties marked with the `@Published` attribute.
|
||||
public struct Publisher: OpenCombine.Publisher {
|
||||
|
||||
@@ -37,38 +90,100 @@ public struct Published<Value> {
|
||||
|
||||
public typealias Failure = Never
|
||||
|
||||
fileprivate let subject: PublishedSubject<Value>
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Value, Downstream.Failure == Never
|
||||
{
|
||||
subject.subscribe(subscriber)
|
||||
}
|
||||
|
||||
fileprivate let subject: OpenCombine.CurrentValueSubject<Value, Never>
|
||||
|
||||
fileprivate init(_ output: Output) {
|
||||
subject = .init(output)
|
||||
}
|
||||
}
|
||||
|
||||
private var value: Value
|
||||
private enum Storage {
|
||||
case value(Value)
|
||||
case publisher(Publisher)
|
||||
}
|
||||
@propertyWrapper
|
||||
private final class Box {
|
||||
var wrappedValue: Storage
|
||||
|
||||
private var publisher: Publisher?
|
||||
|
||||
internal var objectWillChange: ObservableObjectPublisher?
|
||||
|
||||
/// The property that can be accessed with the `$` syntax and allows access to
|
||||
/// the `Publisher`
|
||||
public var projectedValue: Publisher {
|
||||
mutating get {
|
||||
if let publisher = publisher {
|
||||
return publisher
|
||||
}
|
||||
let publisher = Publisher(value)
|
||||
self.publisher = publisher
|
||||
return publisher
|
||||
init(wrappedValue: Storage) {
|
||||
self.wrappedValue = wrappedValue
|
||||
}
|
||||
}
|
||||
|
||||
@Box private var storage: Storage
|
||||
|
||||
internal var objectWillChange: ObservableObjectPublisher? {
|
||||
get {
|
||||
switch storage {
|
||||
case .value:
|
||||
return nil
|
||||
case .publisher(let publisher):
|
||||
return publisher.subject.objectWillChange
|
||||
}
|
||||
}
|
||||
nonmutating set {
|
||||
getPublisher().subject.objectWillChange = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the published instance with an initial wrapped value.
|
||||
///
|
||||
/// Don't use this initializer directly. Instead, create a property with
|
||||
/// the `@Published` attribute, as shown here:
|
||||
///
|
||||
/// @Published var lastUpdated: Date = Date()
|
||||
///
|
||||
/// - Parameter wrappedValue: The publisher's initial value.
|
||||
public init(initialValue: Value) {
|
||||
self.init(wrappedValue: initialValue)
|
||||
}
|
||||
|
||||
/// Creates the published instance with an initial value.
|
||||
///
|
||||
/// Don't use this initializer directly. Instead, create a property with
|
||||
/// the `@Published` attribute, as shown here:
|
||||
///
|
||||
/// @Published var lastUpdated: Date = Date()
|
||||
///
|
||||
/// - Parameter initialValue: The publisher's initial value.
|
||||
public init(wrappedValue: Value) {
|
||||
_storage = Box(wrappedValue: .value(wrappedValue))
|
||||
}
|
||||
|
||||
/// The property for which this instance exposes a publisher.
|
||||
///
|
||||
/// The `projectedValue` is the property accessed with the `$` operator.
|
||||
public var projectedValue: Publisher {
|
||||
mutating get {
|
||||
return getPublisher()
|
||||
}
|
||||
set { // swiftlint:disable:this unused_setter_value
|
||||
switch storage {
|
||||
case .value(let value):
|
||||
storage = .publisher(Publisher(value))
|
||||
case .publisher:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: This method can mutate `storage`
|
||||
internal func getPublisher() -> Publisher {
|
||||
switch storage {
|
||||
case .value(let value):
|
||||
let publisher = Publisher(value)
|
||||
storage = .publisher(publisher)
|
||||
return publisher
|
||||
case .publisher(let publisher):
|
||||
return publisher
|
||||
}
|
||||
}
|
||||
// swiftlint:disable let_var_whitespace
|
||||
@available(*, unavailable, message: """
|
||||
@Published is only available on properties of classes
|
||||
@@ -85,12 +200,20 @@ public struct Published<Value> {
|
||||
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Published<Value>>
|
||||
) -> Value {
|
||||
get {
|
||||
return object[keyPath: storageKeyPath].value
|
||||
switch object[keyPath: storageKeyPath].storage {
|
||||
case .value(let value):
|
||||
return value
|
||||
case .publisher(let publisher):
|
||||
return publisher.subject.value
|
||||
}
|
||||
}
|
||||
set {
|
||||
object[keyPath: storageKeyPath].objectWillChange?.send()
|
||||
object[keyPath: storageKeyPath].publisher?.subject.send(newValue)
|
||||
object[keyPath: storageKeyPath].value = newValue
|
||||
switch object[keyPath: storageKeyPath].storage {
|
||||
case .value:
|
||||
object[keyPath: storageKeyPath].storage = .publisher(Publisher(newValue))
|
||||
case .publisher(let publisher):
|
||||
publisher.subject.value = newValue
|
||||
}
|
||||
}
|
||||
// TODO: Benchmark and explore a possibility to use _modify
|
||||
}
|
||||
|
||||
@@ -7,18 +7,40 @@
|
||||
|
||||
/// Declares that a type can transmit a sequence of values over time.
|
||||
///
|
||||
/// There are four kinds of messages:
|
||||
/// subscription - A connection between `Publisher` and `Subscriber`.
|
||||
/// value - An element in the sequence.
|
||||
/// error - The sequence ended with an error (`.failure(e)`).
|
||||
/// complete - The sequence ended successfully (`.finished`).
|
||||
/// A publisher delivers elements to one or more `Subscriber` instances.
|
||||
/// The subscriber’s `Input` and `Failure` associated types must match the `Output` and
|
||||
/// `Failure` types declared by the publisher.
|
||||
/// The publisher implements the `receive(subscriber:)`method to accept a subscriber.
|
||||
///
|
||||
/// Both `.failure` and `.finished` are terminal messages.
|
||||
/// After this, the publisher can call the following methods on the subscriber:
|
||||
/// - `receive(subscription:)`: Acknowledges the subscribe request and returns
|
||||
/// a `Subscription` instance. The subscriber uses the subscription to demand elements
|
||||
/// from the publisher and can use it to cancel publishing.
|
||||
/// - `receive(_:)`: Delivers one element from the publisher to the subscriber.
|
||||
/// - `receive(completion:)`: Informs the subscriber that publishing has ended,
|
||||
/// either normally or with an error.
|
||||
///
|
||||
/// You can summarize these possibilities with a regular expression:
|
||||
/// value*(error|finished)?
|
||||
/// Every `Publisher` must adhere to this contract for downstream subscribers to function
|
||||
/// correctly.
|
||||
///
|
||||
/// Every `Publisher` must adhere to this contract.
|
||||
/// Extensions on `Publisher` define a wide variety of _operators_ that you compose to
|
||||
/// create sophisticated event-processing chains.
|
||||
/// Each operator returns a type that implements the `Publisher` protocol
|
||||
/// Most of these types exist as extensions on the `Publishers` enumeration.
|
||||
/// For example, the `map(_:)` operator returns an instance of `Publishers.Map`.
|
||||
///
|
||||
/// # Creating Your Own Publishers
|
||||
///
|
||||
/// Rather than implementing the `Publisher` protocol yourself, you can create your own
|
||||
/// publisher by using one of several types provided by the OpenCombine framework:
|
||||
///
|
||||
/// - Use a concrete subclass of `Subject`, such as `PassthroughSubject`, to publish
|
||||
/// values on-demand by calling its `send(_:)` method.
|
||||
/// - Use a `CurrentValueSubject` to publish whenever you update the subject’s underlying
|
||||
/// value.
|
||||
/// - Add the `@Published` annotation to a property of one of your own types. In doing so,
|
||||
/// the property gains a publisher that emits an event whenever the property’s value
|
||||
/// changes. See the `Published` type for an example of this approach.
|
||||
public protocol Publisher {
|
||||
|
||||
/// The kind of values published by this publisher.
|
||||
@@ -29,13 +51,15 @@ public protocol Publisher {
|
||||
/// Use `Never` if this `Publisher` does not publish errors.
|
||||
associatedtype Failure: Error
|
||||
|
||||
/// This function is called to attach the specified `Subscriber` to this `Publisher`
|
||||
/// by `subscribe(_:)`
|
||||
/// Attaches the specified subscriber to this publisher.
|
||||
///
|
||||
/// - SeeAlso: `subscribe(_:)`
|
||||
/// - Parameters:
|
||||
/// - subscriber: The subscriber to attach to this `Publisher`.
|
||||
/// once attached it can begin to receive values.
|
||||
/// Always call this function instead of `receive(subscriber:)`.
|
||||
/// Adopters of `Publisher` must implement `receive(subscriber:)`. The implementation
|
||||
/// of `subscribe(_:)` provided by `Publisher` calls through to
|
||||
/// `receive(subscriber:)`.
|
||||
///
|
||||
/// - Parameter subscriber: The subscriber to attach to this publisher. After
|
||||
/// attaching, the subscriber can start to receive values.
|
||||
func receive<Subscriber: OpenCombine.Subscriber>(subscriber: Subscriber)
|
||||
where Failure == Subscriber.Failure, Output == Subscriber.Input
|
||||
}
|
||||
@@ -55,9 +79,27 @@ extension Publisher {
|
||||
public func subscribe<Subscriber: OpenCombine.Subscriber>(_ subscriber: Subscriber)
|
||||
where Failure == Subscriber.Failure, Output == Subscriber.Input
|
||||
{
|
||||
receive(subscriber: subscriber)
|
||||
if let hook = DebugHook.getGlobalHook() {
|
||||
if var marker = subscriber as? SubscriberTapMarker {
|
||||
let anySubscriber = marker.inner
|
||||
as! AnySubscriber<Subscriber.Input, Subscriber.Failure>
|
||||
hook.willReceive(publisher: self, subscriber: anySubscriber)
|
||||
receive(subscriber: subscriber)
|
||||
hook.didReceive(publisher: self, subscriber: anySubscriber)
|
||||
} else {
|
||||
let tap = SubscriberTap(subscriber: subscriber)
|
||||
hook.willReceive(publisher: self, subscriber: subscriber)
|
||||
receive(subscriber: tap)
|
||||
hook.didReceive(publisher: self, subscriber: subscriber)
|
||||
}
|
||||
} else {
|
||||
receive(subscriber: subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attaches the specified subject to this publisher.
|
||||
///
|
||||
/// - Parameter subject: The subject to attach to this publisher.
|
||||
public func subscribe<Subject: OpenCombine.Subject>(
|
||||
_ subject: Subject
|
||||
) -> AnyCancellable
|
||||
|
||||
@@ -17,7 +17,7 @@ public struct Deferred<DeferredPublisher: Publisher>: Publisher {
|
||||
/// Use `Never` if this `Publisher` does not publish errors.
|
||||
public typealias Failure = DeferredPublisher.Failure
|
||||
|
||||
/// The closure to execute when it receives a subscription.
|
||||
/// The closure to execute when this deferred publisher receives a subscription.
|
||||
///
|
||||
/// The publisher returned by this closure immediately
|
||||
/// receives the incoming subscription.
|
||||
|
||||
@@ -24,6 +24,7 @@ public struct Empty<Output, Failure: Error>: Publisher, Equatable {
|
||||
///
|
||||
/// Use this initializer to connect the empty publisher to subscribers or other
|
||||
/// publishers that have specific output and failure types.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - completeImmediately: A Boolean value that indicates whether the publisher
|
||||
/// should immediately finish.
|
||||
|
||||
@@ -20,6 +20,7 @@ public struct Fail<Output, Failure: Error>: Publisher {
|
||||
///
|
||||
/// Use this initializer to create a `Fail` publisher that can work with
|
||||
/// subscribers or publishers that expect a given output type.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - outputType: The output type exposed by this publisher.
|
||||
/// - failure: The failure to send when terminating the publisher.
|
||||
|
||||
@@ -0,0 +1,673 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ┃
|
||||
// ┃ Auto-generated from GYB template. DO NOT EDIT! ┃
|
||||
// ┃ ┃
|
||||
// ┃ ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
//
|
||||
// Publishers.Catch.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 25.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Handles errors from an upstream publisher by replacing it with another publisher.
|
||||
///
|
||||
/// Use `catch()` to replace an error from an upstream publisher with a new publisher.
|
||||
///
|
||||
/// In the example below, the `catch()` operator handles the `SimpleError` thrown by
|
||||
/// the upstream publisher by replacing the error with a `Just` publisher. This
|
||||
/// continues the stream by publishing a single value and completing normally.
|
||||
///
|
||||
/// struct SimpleError: Error {}
|
||||
/// let numbers = [5, 4, 3, 2, 1, 0, 9, 8, 7, 6]
|
||||
/// cancellable = numbers.publisher
|
||||
/// .tryLast(where: {
|
||||
/// guard $0 != 0 else { throw SimpleError() }
|
||||
/// return true
|
||||
/// })
|
||||
/// .catch { error in
|
||||
/// Just(-1)
|
||||
/// }
|
||||
/// .sink { print("\($0)") }
|
||||
/// // Prints: -1
|
||||
///
|
||||
/// Backpressure note: This publisher passes through `request` and `cancel` to
|
||||
/// the upstream. After receiving an error, the publisher sends sends any unfulfilled
|
||||
/// demand to the new `Publisher`.
|
||||
///
|
||||
/// - SeeAlso: `replaceError`
|
||||
/// - Parameter handler: A closure that accepts the upstream failure as input and
|
||||
/// returns a publisher to replace the upstream publisher.
|
||||
/// - Returns: A publisher that handles errors from an upstream publisher by replacing
|
||||
/// the failed publisher with another publisher.
|
||||
public func `catch`<NewPublisher: Publisher>(
|
||||
_ handler: @escaping (Failure) -> NewPublisher
|
||||
) -> Publishers.Catch<Self, NewPublisher>
|
||||
where NewPublisher.Output == Output
|
||||
{
|
||||
return .init(upstream: self, handler: handler)
|
||||
}
|
||||
|
||||
/// Handles errors from an upstream publisher by either replacing it with another
|
||||
/// publisher or throwing a new error.
|
||||
///
|
||||
/// Use `tryCatch(_:)` to decide how to handle from an upstream publisher by either
|
||||
/// replacing the publisher with a new publisher, or throwing a new error.
|
||||
///
|
||||
/// In the example below, an array publisher emits values that a `tryMap(_:)` operator
|
||||
/// evaluates to ensure the values are greater than zero. If the values aren’t greater
|
||||
/// than zero, the operator throws an error to the downstream subscriber to let it
|
||||
/// know there was a problem. The subscriber, `tryCatch(_:)`, replaces the error with
|
||||
/// a new publisher using ``Just`` to publish a final value before the stream ends
|
||||
/// normally.
|
||||
///
|
||||
/// enum SimpleError: Error { case error }
|
||||
/// var numbers = [5, 4, 3, 2, 1, -1, 7, 8, 9, 10]
|
||||
///
|
||||
/// cancellable = numbers.publisher
|
||||
/// .tryMap { v in
|
||||
/// if v > 0 {
|
||||
/// return v
|
||||
/// } else {
|
||||
/// throw SimpleError.error
|
||||
/// }
|
||||
/// }
|
||||
/// .tryCatch { error in
|
||||
/// Just(0) // Send a final value before completing normally.
|
||||
/// // Alternatively, throw a new error to terminate the stream.
|
||||
/// }
|
||||
/// .sink(receiveCompletion: { print ("Completion: \($0).") },
|
||||
/// receiveValue: { print ("Received \($0).") })
|
||||
/// // Received 5.
|
||||
/// // Received 4.
|
||||
/// // Received 3.
|
||||
/// // Received 2.
|
||||
/// // Received 1.
|
||||
/// // Received 0.
|
||||
/// // Completion: finished.
|
||||
///
|
||||
/// - Parameter handler: A throwing closure that accepts the upstream failure as
|
||||
/// input. This closure can either replace the upstream publisher with a new one,
|
||||
/// or throw a new error to the downstream subscriber.
|
||||
/// - Returns: A publisher that handles errors from an upstream publisher by replacing
|
||||
/// the failed publisher with another publisher, or an error.
|
||||
public func tryCatch<NewPublisher: Publisher>(
|
||||
_ handler: @escaping (Failure) throws -> NewPublisher
|
||||
) -> Publishers.TryCatch<Self, NewPublisher>
|
||||
where NewPublisher.Output == Output
|
||||
{
|
||||
return .init(upstream: self, handler: handler)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that handles errors from an upstream publisher by replacing the failed
|
||||
/// publisher with another publisher.
|
||||
public struct Catch<Upstream: Publisher, NewPublisher: Publisher>: Publisher
|
||||
where Upstream.Output == NewPublisher.Output
|
||||
{
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = NewPublisher.Failure
|
||||
|
||||
/// The publisher that this publisher receives elements from.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// A closure that accepts the upstream failure as input and returns a publisher
|
||||
/// to replace the upstream publisher.
|
||||
public let handler: (Upstream.Failure) -> NewPublisher
|
||||
|
||||
/// Creates a publisher that handles errors from an upstream publisher by
|
||||
/// replacing the failed publisher with another publisher.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - upstream: The publisher that this publisher receives elements from.
|
||||
/// - handler: A closure that accepts the upstream failure as input and returns
|
||||
/// a publisher to replace the upstream publisher.
|
||||
public init(upstream: Upstream,
|
||||
handler: @escaping (Upstream.Failure) -> NewPublisher) {
|
||||
self.upstream = upstream
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
let inner = Inner(downstream: subscriber, handler: handler)
|
||||
let uncaughtS = Inner.UncaughtS(inner: inner)
|
||||
upstream.subscribe(uncaughtS)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher that handles errors from an upstream publisher by replacing
|
||||
/// the failed publisher with another publisher or producing a new error.
|
||||
///
|
||||
/// Because this publisher’s handler can throw an error, `Publishers.TryCatch` defines
|
||||
/// its `Failure` type as `Error`. This is different from `Publishers.Catch`, which
|
||||
/// gets its failure type from the replacement publisher.
|
||||
public struct TryCatch<Upstream: Publisher, NewPublisher: Publisher>: Publisher
|
||||
where Upstream.Output == NewPublisher.Output
|
||||
{
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Error
|
||||
|
||||
/// The publisher that this publisher receives elements from.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// A closure that accepts the upstream failure as input and either returns
|
||||
/// a publisher to replace the upstream publisher or throws an error.
|
||||
public let handler: (Upstream.Failure) throws -> NewPublisher
|
||||
|
||||
/// Creates a publisher that handles errors from an upstream publisher by
|
||||
/// replacing the failed publisher with another publisher or by throwing an error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - upstream: The publisher that this publisher receives elements from.
|
||||
/// - handler: A closure that accepts the upstream failure as input and either
|
||||
/// returns a publisher to replace the upstream publisher. If this closure
|
||||
/// throws an error, the publisher terminates with the thrown error.
|
||||
public init(upstream: Upstream,
|
||||
handler: @escaping (Upstream.Failure) throws -> NewPublisher) {
|
||||
self.upstream = upstream
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
let inner = Inner(downstream: subscriber, handler: handler)
|
||||
let uncaughtS = Inner.UncaughtS(inner: inner)
|
||||
upstream.subscribe(uncaughtS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.Catch {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Upstream.Output,
|
||||
Downstream.Failure == NewPublisher.Failure
|
||||
{
|
||||
struct UncaughtS: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
let inner: Inner
|
||||
|
||||
var combineIdentifier: CombineIdentifier { return inner.combineIdentifier }
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
inner.receivePre(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return inner.receivePre(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
return inner.receivePre(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return inner.description }
|
||||
|
||||
var customMirror: Mirror { return inner.customMirror }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
|
||||
struct CaughtS: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
typealias Input = NewPublisher.Output
|
||||
|
||||
typealias Failure = NewPublisher.Failure
|
||||
|
||||
let inner: Inner
|
||||
|
||||
var combineIdentifier: CombineIdentifier { return inner.combineIdentifier }
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
inner.receivePost(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return inner.receivePost(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
inner.receivePost(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return inner.description }
|
||||
|
||||
var customMirror: Mirror { return inner.customMirror }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
|
||||
private enum State {
|
||||
case pendingPre
|
||||
case pre(Subscription)
|
||||
case pendingPost
|
||||
case post(Subscription)
|
||||
case cancelled
|
||||
}
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var demand = Subscribers.Demand.none
|
||||
|
||||
private var state = State.pendingPre
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
private let handler: (Upstream.Failure) -> NewPublisher
|
||||
|
||||
init(downstream: Downstream,
|
||||
handler: @escaping (Upstream.Failure) -> NewPublisher) {
|
||||
self.downstream = downstream
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receivePre(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .pendingPre = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .pre(subscription)
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receivePre(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
demand -= 1
|
||||
lock.unlock()
|
||||
let newDemand = downstream.receive(input)
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
return newDemand
|
||||
}
|
||||
|
||||
func receivePre(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pre:
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
downstream.receive(completion: .finished)
|
||||
case .pendingPre, .pendingPost, .post, .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
case .failure(let error):
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pre:
|
||||
state = .pendingPost
|
||||
lock.unlock()
|
||||
handler(error).subscribe(CaughtS(inner: self))
|
||||
case .cancelled:
|
||||
lock.unlock()
|
||||
case .pendingPre, .post, .pendingPost:
|
||||
completionBeforeSubscription()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receivePost(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .pendingPost = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .post(subscription)
|
||||
let demand = self.demand
|
||||
lock.unlock()
|
||||
if demand > 0 {
|
||||
subscription.request(demand)
|
||||
}
|
||||
}
|
||||
|
||||
func receivePost(_ input: NewPublisher.Output) -> Subscribers.Demand {
|
||||
return downstream.receive(input)
|
||||
}
|
||||
|
||||
func receivePost(completion: Subscribers.Completion<NewPublisher.Failure>) {
|
||||
lock.lock()
|
||||
guard case .post = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pendingPre:
|
||||
// The client is only able to call the `request` method after we've sent
|
||||
// `self` downstream. We only do it in the `receivePre(subscription:)`
|
||||
// method, after setting `state` to `pre`.
|
||||
// After that `state` never becomes `pendingPre`.
|
||||
requestBeforeSubscription()
|
||||
case let .pre(subscription):
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
case .pendingPost:
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
case let .post(subscription):
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
case .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
switch state {
|
||||
case let .pre(subscription), let .post(subscription):
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
case .pendingPre, .pendingPost, .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
var description: String { return "Catch" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
let children: [Mirror.Child] = [
|
||||
("downstream", downstream),
|
||||
("demand", demand)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.TryCatch {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Upstream.Output,
|
||||
Downstream.Failure == Error
|
||||
{
|
||||
struct UncaughtS: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
let inner: Inner
|
||||
|
||||
var combineIdentifier: CombineIdentifier { return inner.combineIdentifier }
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
inner.receivePre(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return inner.receivePre(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
return inner.receivePre(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return inner.description }
|
||||
|
||||
var customMirror: Mirror { return inner.customMirror }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
|
||||
struct CaughtS: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
typealias Input = NewPublisher.Output
|
||||
|
||||
typealias Failure = NewPublisher.Failure
|
||||
|
||||
let inner: Inner
|
||||
|
||||
var combineIdentifier: CombineIdentifier { return inner.combineIdentifier }
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
inner.receivePost(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return inner.receivePost(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
inner.receivePost(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return inner.description }
|
||||
|
||||
var customMirror: Mirror { return inner.customMirror }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
|
||||
private enum State {
|
||||
case pendingPre
|
||||
case pre(Subscription)
|
||||
case pendingPost
|
||||
case post(Subscription)
|
||||
case cancelled
|
||||
}
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var demand = Subscribers.Demand.none
|
||||
|
||||
private var state = State.pendingPre
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
private let handler: (Upstream.Failure) throws -> NewPublisher
|
||||
|
||||
init(downstream: Downstream,
|
||||
handler: @escaping (Upstream.Failure) throws -> NewPublisher) {
|
||||
self.downstream = downstream
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receivePre(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .pendingPre = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .pre(subscription)
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receivePre(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
demand -= 1
|
||||
lock.unlock()
|
||||
let newDemand = downstream.receive(input)
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
return newDemand
|
||||
}
|
||||
|
||||
func receivePre(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pre:
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
downstream.receive(completion: .finished)
|
||||
case .pendingPre, .pendingPost, .post, .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
case .failure(let error):
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pre:
|
||||
state = .pendingPost
|
||||
lock.unlock()
|
||||
do {
|
||||
try handler(error).subscribe(CaughtS(inner: self))
|
||||
} catch let anotherError {
|
||||
lock.lock()
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
downstream.receive(completion: .failure(anotherError))
|
||||
}
|
||||
case .cancelled:
|
||||
lock.unlock()
|
||||
case .pendingPre, .post, .pendingPost:
|
||||
completionBeforeSubscription()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receivePost(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .pendingPost = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .post(subscription)
|
||||
let demand = self.demand
|
||||
lock.unlock()
|
||||
if demand > 0 {
|
||||
subscription.request(demand)
|
||||
}
|
||||
}
|
||||
|
||||
func receivePost(_ input: NewPublisher.Output) -> Subscribers.Demand {
|
||||
return downstream.receive(input)
|
||||
}
|
||||
|
||||
func receivePost(completion: Subscribers.Completion<NewPublisher.Failure>) {
|
||||
lock.lock()
|
||||
guard case .post = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion.eraseError())
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pendingPre:
|
||||
// The client is only able to call the `request` method after we've sent
|
||||
// `self` downstream. We only do it in the `receivePre(subscription:)`
|
||||
// method, after setting `state` to `pre`.
|
||||
// After that `state` never becomes `pendingPre`.
|
||||
requestBeforeSubscription()
|
||||
case let .pre(subscription):
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
case .pendingPost:
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
case let .post(subscription):
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
case .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
switch state {
|
||||
case let .pre(subscription), let .post(subscription):
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
case .pendingPre, .pendingPost, .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
var description: String { return "TryCatch" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
let children: [Mirror.Child] = [
|
||||
("downstream", downstream),
|
||||
("demand", demand)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
private func completionBeforeSubscription(file: StaticString = #file,
|
||||
line: UInt = #line) -> Never {
|
||||
fatalError("Unexpected state: received completion but do not have subscription",
|
||||
file: file,
|
||||
line: line)
|
||||
}
|
||||
|
||||
private func requestBeforeSubscription(file: StaticString = #file,
|
||||
line: UInt = #line) -> Never {
|
||||
fatalError("Unexpected state: request before subscription sent",
|
||||
file: file,
|
||||
line: line)
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ┃
|
||||
// ┃ Auto-generated from GYB template. DO NOT EDIT! ┃
|
||||
// ┃ ┃
|
||||
// ┃ ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
//
|
||||
// Publishers.CombineLatest.swift.gyb
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
// swiftlint:disable generic_type_name
|
||||
// swiftlint:disable large_tuple
|
||||
|
||||
// MARK: - CombineLatest methods on Publisher
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Subscribes to an additional publisher and publishes a tuple upon
|
||||
/// receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - other: Another publisher to combine with this one.
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
public func combineLatest<P: Publisher>(
|
||||
_ other: P
|
||||
) -> Publishers.CombineLatest<Self, P>
|
||||
where Failure == P.Failure
|
||||
{
|
||||
return .init(self, other)
|
||||
}
|
||||
|
||||
/// Subscribes to an additional publisher and invokes a closure
|
||||
/// upon receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - other: Another publisher to combine with this one.
|
||||
/// - transform: A closure that receives the most recent value from each publisher
|
||||
/// and returns a new value to publish.
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
public func combineLatest<P: Publisher, Result>(
|
||||
_ other: P,
|
||||
_ transform: @escaping (Output, P.Output) -> Result
|
||||
) -> Publishers.Map<Publishers.CombineLatest<Self, P>, Result>
|
||||
where Failure == P.Failure
|
||||
{
|
||||
return Publishers.CombineLatest(self, other).map {
|
||||
transform($0, $1)
|
||||
}
|
||||
}
|
||||
/// Subscribes to two additional publishers and publishes a tuple upon
|
||||
/// receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - publisher1: A second publisher to combine with this one.
|
||||
/// - publisher2: A third publisher to combine with this one.
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
public func combineLatest<P: Publisher, Q: Publisher>(
|
||||
_ publisher1: P,
|
||||
_ publisher2: Q
|
||||
) -> Publishers.CombineLatest3<Self, P, Q>
|
||||
where Failure == P.Failure,
|
||||
P.Failure == Q.Failure
|
||||
{
|
||||
return .init(self, publisher1, publisher2)
|
||||
}
|
||||
|
||||
/// Subscribes to two additional publishers and invokes a closure
|
||||
/// upon receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - publisher1: A second publisher to combine with this one.
|
||||
/// - publisher2: A third publisher to combine with this one.
|
||||
/// - transform: A closure that receives the most recent value from each publisher
|
||||
/// and returns a new value to publish.
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
public func combineLatest<P: Publisher, Q: Publisher, Result>(
|
||||
_ publisher1: P,
|
||||
_ publisher2: Q,
|
||||
_ transform: @escaping (Output, P.Output, Q.Output) -> Result
|
||||
) -> Publishers.Map<Publishers.CombineLatest3<Self, P, Q>, Result>
|
||||
where Failure == P.Failure,
|
||||
P.Failure == Q.Failure
|
||||
{
|
||||
return Publishers.CombineLatest3(self, publisher1, publisher2).map {
|
||||
transform($0, $1, $2)
|
||||
}
|
||||
}
|
||||
/// Subscribes to three additional publishers and publishes a tuple upon
|
||||
/// receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - publisher1: A second publisher to combine with this one.
|
||||
/// - publisher2: A third publisher to combine with this one.
|
||||
/// - publisher3: A fourth publisher to combine with this one.
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
public func combineLatest<P: Publisher, Q: Publisher, R: Publisher>(
|
||||
_ publisher1: P,
|
||||
_ publisher2: Q,
|
||||
_ publisher3: R
|
||||
) -> Publishers.CombineLatest4<Self, P, Q, R>
|
||||
where Failure == P.Failure,
|
||||
P.Failure == Q.Failure,
|
||||
Q.Failure == R.Failure
|
||||
{
|
||||
return .init(self, publisher1, publisher2, publisher3)
|
||||
}
|
||||
|
||||
/// Subscribes to three additional publishers and invokes a closure
|
||||
/// upon receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - publisher1: A second publisher to combine with this one.
|
||||
/// - publisher2: A third publisher to combine with this one.
|
||||
/// - publisher3: A fourth publisher to combine with this one.
|
||||
/// - transform: A closure that receives the most recent value from each publisher
|
||||
/// and returns a new value to publish.
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
public func combineLatest<P: Publisher, Q: Publisher, R: Publisher, Result>(
|
||||
_ publisher1: P,
|
||||
_ publisher2: Q,
|
||||
_ publisher3: R,
|
||||
_ transform: @escaping (Output, P.Output, Q.Output, R.Output) -> Result
|
||||
) -> Publishers.Map<Publishers.CombineLatest4<Self, P, Q, R>, Result>
|
||||
where Failure == P.Failure,
|
||||
P.Failure == Q.Failure,
|
||||
Q.Failure == R.Failure
|
||||
{
|
||||
return Publishers.CombineLatest4(self, publisher1, publisher2, publisher3).map {
|
||||
transform($0, $1, $2, $3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CombineLatest publishers
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that receives and combines the latest elements from two
|
||||
/// publishers.
|
||||
public struct CombineLatest<A: Publisher, B: Publisher>
|
||||
: Publisher
|
||||
where A.Failure == B.Failure
|
||||
{
|
||||
public typealias Output = (A.Output, B.Output)
|
||||
|
||||
public typealias Failure = A.Failure
|
||||
|
||||
public let a: A
|
||||
|
||||
public let b: B
|
||||
|
||||
public init(
|
||||
_ a: A,
|
||||
_ b: B
|
||||
) {
|
||||
self.a = a
|
||||
self.b = b
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Failure == Failure,
|
||||
Downstream.Input == Output
|
||||
{
|
||||
typealias Inner = CombineLatest2Inner<A.Output,
|
||||
B.Output,
|
||||
Failure,
|
||||
Downstream>
|
||||
let inner = Inner(downstream: subscriber, upstreamCount: 2)
|
||||
a.subscribe(Inner.Side(index: 0, combiner: inner))
|
||||
b.subscribe(Inner.Side(index: 1, combiner: inner))
|
||||
subscriber.receive(subscription: inner)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher that receives and combines the latest elements from three
|
||||
/// publishers.
|
||||
public struct CombineLatest3<A: Publisher, B: Publisher, C: Publisher>
|
||||
: Publisher
|
||||
where A.Failure == B.Failure,
|
||||
B.Failure == C.Failure
|
||||
{
|
||||
public typealias Output = (A.Output, B.Output, C.Output)
|
||||
|
||||
public typealias Failure = A.Failure
|
||||
|
||||
public let a: A
|
||||
|
||||
public let b: B
|
||||
|
||||
public let c: C
|
||||
|
||||
public init(
|
||||
_ a: A,
|
||||
_ b: B,
|
||||
_ c: C
|
||||
) {
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Failure == Failure,
|
||||
Downstream.Input == Output
|
||||
{
|
||||
typealias Inner = CombineLatest3Inner<A.Output,
|
||||
B.Output,
|
||||
C.Output,
|
||||
Failure,
|
||||
Downstream>
|
||||
let inner = Inner(downstream: subscriber, upstreamCount: 3)
|
||||
a.subscribe(Inner.Side(index: 0, combiner: inner))
|
||||
b.subscribe(Inner.Side(index: 1, combiner: inner))
|
||||
c.subscribe(Inner.Side(index: 2, combiner: inner))
|
||||
subscriber.receive(subscription: inner)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher that receives and combines the latest elements from four
|
||||
/// publishers.
|
||||
public struct CombineLatest4<A: Publisher, B: Publisher, C: Publisher, D: Publisher>
|
||||
: Publisher
|
||||
where A.Failure == B.Failure,
|
||||
B.Failure == C.Failure,
|
||||
C.Failure == D.Failure
|
||||
{
|
||||
public typealias Output = (A.Output, B.Output, C.Output, D.Output)
|
||||
|
||||
public typealias Failure = A.Failure
|
||||
|
||||
public let a: A
|
||||
|
||||
public let b: B
|
||||
|
||||
public let c: C
|
||||
|
||||
public let d: D
|
||||
|
||||
public init(
|
||||
_ a: A,
|
||||
_ b: B,
|
||||
_ c: C,
|
||||
_ d: D
|
||||
) {
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
self.d = d
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Failure == Failure,
|
||||
Downstream.Input == Output
|
||||
{
|
||||
typealias Inner = CombineLatest4Inner<A.Output,
|
||||
B.Output,
|
||||
C.Output,
|
||||
D.Output,
|
||||
Failure,
|
||||
Downstream>
|
||||
let inner = Inner(downstream: subscriber, upstreamCount: 4)
|
||||
a.subscribe(Inner.Side(index: 0, combiner: inner))
|
||||
b.subscribe(Inner.Side(index: 1, combiner: inner))
|
||||
c.subscribe(Inner.Side(index: 2, combiner: inner))
|
||||
d.subscribe(Inner.Side(index: 3, combiner: inner))
|
||||
subscriber.receive(subscription: inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable conformances
|
||||
|
||||
extension Publishers.CombineLatest: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable {}
|
||||
|
||||
extension Publishers.CombineLatest3: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable {}
|
||||
|
||||
extension Publishers.CombineLatest4: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable,
|
||||
D: Equatable {}
|
||||
|
||||
// MARK: - Inners
|
||||
|
||||
private final class CombineLatest2Inner<Input0,
|
||||
Input1,
|
||||
Failure,
|
||||
Downstream: Subscriber>
|
||||
: AbstractCombineLatest<(Input0, Input1), Failure, Downstream>
|
||||
where Downstream.Input == (Input0, Input1),
|
||||
Downstream.Failure == Failure
|
||||
{
|
||||
override func convert(values: [Any?]) -> (Input0, Input1) {
|
||||
return (values[0] as! Input0,
|
||||
values[1] as! Input1)
|
||||
}
|
||||
}
|
||||
|
||||
private final class CombineLatest3Inner<Input0,
|
||||
Input1,
|
||||
Input2,
|
||||
Failure,
|
||||
Downstream: Subscriber>
|
||||
: AbstractCombineLatest<(Input0, Input1, Input2), Failure, Downstream>
|
||||
where Downstream.Input == (Input0, Input1, Input2),
|
||||
Downstream.Failure == Failure
|
||||
{
|
||||
override func convert(values: [Any?]) -> (Input0, Input1, Input2) {
|
||||
return (values[0] as! Input0,
|
||||
values[1] as! Input1,
|
||||
values[2] as! Input2)
|
||||
}
|
||||
}
|
||||
|
||||
private final class CombineLatest4Inner<Input0,
|
||||
Input1,
|
||||
Input2,
|
||||
Input3,
|
||||
Failure,
|
||||
Downstream: Subscriber>
|
||||
: AbstractCombineLatest<(Input0, Input1, Input2, Input3), Failure, Downstream>
|
||||
where Downstream.Input == (Input0, Input1, Input2, Input3),
|
||||
Downstream.Failure == Failure
|
||||
{
|
||||
override func convert(values: [Any?]) -> (Input0, Input1, Input2, Input3) {
|
||||
return (values[0] as! Input0,
|
||||
values[1] as! Input1,
|
||||
values[2] as! Input2,
|
||||
values[3] as! Input3)
|
||||
}
|
||||
}
|
||||
+158
-20
@@ -13,16 +13,100 @@
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Encodes the output from upstream using a specified `TopLevelEncoder`.
|
||||
/// For example, use `JSONEncoder`.
|
||||
/// Encodes the output from upstream using a specified encoder.
|
||||
///
|
||||
/// Use `encode(encoder:)` with a `JSONDecoder` (or a `PropertyListDecoder` for
|
||||
/// property lists) to encode an `Encodable` struct into `Data` that could be used to
|
||||
/// make a JSON string (or written to disk as a binary plist in the case of property
|
||||
/// lists).
|
||||
///
|
||||
/// In this example, a `PassthroughSubject` publishes an `Article`.
|
||||
/// The `encode(encoder:)` operator encodes the properties of the `Article` struct
|
||||
/// into a new JSON string according to the `Codable` protocol adopted by `Article`.
|
||||
/// The operator publishes the resulting JSON string to the downstream subscriber.
|
||||
/// If the encoding operation fails, which can happen in the case of complex
|
||||
/// properties that can’t be directly transformed into JSON, the stream terminates
|
||||
/// and the error is passed to the downstream subscriber.
|
||||
///
|
||||
/// struct Article: Codable {
|
||||
/// let title: String
|
||||
/// let author: String
|
||||
/// let pubDate: Date
|
||||
/// }
|
||||
///
|
||||
/// let dataProvider = PassthroughSubject<Article, Never>()
|
||||
/// let cancellable = dataProvider
|
||||
/// .encode(encoder: JSONEncoder())
|
||||
/// .sink(receiveCompletion: { print ("Completion: \($0)") },
|
||||
/// receiveValue: { data in
|
||||
/// guard let stringRepresentation =
|
||||
/// String(data: data, encoding: .utf8) else { return }
|
||||
/// print("""
|
||||
/// Data received \(data) string representation: \
|
||||
/// \(stringRepresentation)
|
||||
/// """)
|
||||
/// })
|
||||
///
|
||||
/// dataProvider.send(Article(title: "My First Article",
|
||||
/// author: "Gita Kumar",
|
||||
/// pubDate: Date()))
|
||||
///
|
||||
/// // Prints: "Data received 86 bytes string representation:
|
||||
/// // {"title":"My First Article","author":"Gita Kumar"
|
||||
/// // "pubDate":606211803.279603}"
|
||||
///
|
||||
/// - Parameter encoder: An encoder that implements the `TopLevelEncoder` protocol.
|
||||
/// - Returns: A publisher that encodes received elements using a specified encoder,
|
||||
/// and publishes the resulting data.
|
||||
public func encode<Coder: TopLevelEncoder>(
|
||||
encoder: Coder
|
||||
) -> Publishers.Encode<Self, Coder> {
|
||||
return .init(upstream: self, encoder: encoder)
|
||||
}
|
||||
|
||||
/// Decodes the output from upstream using a specified `TopLevelDecoder`.
|
||||
/// For example, use `JSONDecoder`.
|
||||
/// Decodes the output from the upstream using a specified decoder.
|
||||
///
|
||||
/// Use `decode(type:decoder:)` with a `JSONDecoder` (or a `PropertyListDecoder` for
|
||||
/// property lists) to decode data received from a `URLSession.DataTaskPublisher` or
|
||||
/// other data source using the `Decodable` protocol.
|
||||
///
|
||||
/// In this example, a `PassthroughSubject` publishes a JSON string. The JSON decoder
|
||||
/// parses the string, converting its fields according to the `Decodable` protocol
|
||||
/// implemented by `Article`, and successfully populating a new `Article`.
|
||||
/// The `Publishers.Decode` publisher then publishes the `Article` to the downstream.
|
||||
/// If a decoding operation fails, which happens in the case of missing or malformed
|
||||
/// data in the source JSON string, the stream terminates and passes the error to
|
||||
/// the downstream subscriber.
|
||||
///
|
||||
/// struct Article: Codable {
|
||||
/// let title: String
|
||||
/// let author: String
|
||||
/// let pubDate: Date
|
||||
/// }
|
||||
///
|
||||
/// let dataProvider = PassthroughSubject<Data, Never>()
|
||||
/// cancellable = dataProvider
|
||||
/// .decode(type: Article.self, decoder: JSONDecoder())
|
||||
/// .sink(receiveCompletion: { print ("Completion: \($0)")},
|
||||
/// receiveValue: { print ("value: \($0)") })
|
||||
///
|
||||
/// dataProvider.send(Data("""
|
||||
/// {\"pubDate\":1574273638.575666, \
|
||||
/// \"title\" : \"My First Article\", \
|
||||
/// \"author\" : \"Gita Kumar\" }
|
||||
/// """.utf8))
|
||||
///
|
||||
/// // Prints:
|
||||
/// // ".sink() data received Article(title: "My First Article",
|
||||
/// // author: "Gita Kumar",
|
||||
/// // pubDate: 2050-11-20 18:13:58 +0000)"
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - type: The encoded data to decode into a struct that conforms to
|
||||
/// the `Decodable` protocol.
|
||||
/// - decoder: A decoder that implements the `TopLevelDecoder` protocol.
|
||||
/// - Returns: A publisher that decodes a given type using a specified decoder and
|
||||
/// publishes the result.
|
||||
public func decode<Item: Decodable, Coder: TopLevelDecoder>(
|
||||
type: Item.Type,
|
||||
decoder: Coder
|
||||
@@ -88,9 +172,6 @@ extension Publishers.Encode {
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Output, Downstream.Failure == Error
|
||||
{
|
||||
// NOTE: This class has been audited for thread safety.
|
||||
// Combine doesn't use any locking here.
|
||||
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
@@ -99,6 +180,8 @@ extension Publishers.Encode {
|
||||
|
||||
private let encode: (Upstream.Output) throws -> Output
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var finished = false
|
||||
|
||||
private var subscription: Subscription?
|
||||
@@ -111,44 +194,72 @@ extension Publishers.Encode {
|
||||
self.encode = encode
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
if finished || self.subscription != nil {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
self.subscription = subscription
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
if finished { return .none }
|
||||
lock.lock()
|
||||
if finished {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
lock.unlock()
|
||||
do {
|
||||
return try downstream.receive(encode(input))
|
||||
} catch {
|
||||
lock.lock()
|
||||
finished = true
|
||||
let subscription = self.subscription
|
||||
self.subscription = nil
|
||||
lock.unlock()
|
||||
subscription?.cancel()
|
||||
subscription = nil
|
||||
downstream.receive(completion: .failure(error))
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
if finished { return }
|
||||
lock.lock()
|
||||
if finished {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
finished = true
|
||||
subscription = nil
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion.eraseError())
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
let subscription = self.subscription
|
||||
lock.unlock()
|
||||
subscription?.request(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
guard let subscription = self.subscription, !finished else { return }
|
||||
subscription.cancel()
|
||||
lock.lock()
|
||||
guard !finished, let subscription = self.subscription else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.subscription = nil
|
||||
finished = true
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "Encode" }
|
||||
@@ -175,9 +286,6 @@ extension Publishers.Decode {
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Output, Downstream.Failure == Error
|
||||
{
|
||||
// NOTE: This class has been audited for thread safety.
|
||||
// Combine doesn't use any locking here.
|
||||
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
@@ -186,6 +294,8 @@ extension Publishers.Decode {
|
||||
|
||||
private let decode: (Upstream.Output) throws -> Output
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var finished = false
|
||||
|
||||
private var subscription: Subscription?
|
||||
@@ -198,44 +308,72 @@ extension Publishers.Decode {
|
||||
self.decode = decode
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
if finished || self.subscription != nil {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
self.subscription = subscription
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
if finished { return .none }
|
||||
lock.lock()
|
||||
if finished {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
lock.unlock()
|
||||
do {
|
||||
return try downstream.receive(decode(input))
|
||||
} catch {
|
||||
lock.lock()
|
||||
finished = true
|
||||
let subscription = self.subscription
|
||||
self.subscription = nil
|
||||
lock.unlock()
|
||||
subscription?.cancel()
|
||||
subscription = nil
|
||||
downstream.receive(completion: .failure(error))
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
if finished { return }
|
||||
lock.lock()
|
||||
if finished {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
finished = true
|
||||
subscription = nil
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion.eraseError())
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
let subscription = self.subscription
|
||||
lock.unlock()
|
||||
subscription?.request(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
guard let subscription = self.subscription, !finished else { return }
|
||||
subscription.cancel()
|
||||
lock.lock()
|
||||
guard !finished, let subscription = self.subscription else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.subscription = nil
|
||||
finished = true
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "Decode" }
|
||||
|
||||
@@ -12,11 +12,29 @@
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Returns a publisher that publishes the values of a keyt path as a tuple.
|
||||
/// Publishes the value of the key path.
|
||||
///
|
||||
/// In the following example, the `map(_:)` operator uses the Swift
|
||||
/// key path syntax to access the `die` member
|
||||
/// of the `DiceRoll` structure published by the `Just` publisher.
|
||||
///
|
||||
/// The downstream sink subscriber receives only
|
||||
/// the value of this `Int`,
|
||||
/// not the entire `DiceRoll`.
|
||||
///
|
||||
/// struct DiceRoll {
|
||||
/// let die: Int
|
||||
/// }
|
||||
///
|
||||
/// cancellable = Just(DiceRoll(die: Int.random(in: 1...6)))
|
||||
/// .map(\.die)
|
||||
/// .sink {
|
||||
/// print ("Rolled: \($0)")
|
||||
/// }
|
||||
/// // Prints "Rolled: 6 (or some other random value).
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - keyPath: The key path of a property on `Output`
|
||||
/// - keyPath: The key path of a property on `Output`.
|
||||
/// - Returns: A publisher that publishes the value of the key path.
|
||||
public func map<Result>(
|
||||
_ keyPath: KeyPath<Output, Result>
|
||||
@@ -26,12 +44,35 @@ extension Publisher {
|
||||
keyPath: keyPath
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a publisher that publishes the values of two key paths as a tuple.
|
||||
/// Publishes the values of two key paths as a tuple.
|
||||
///
|
||||
/// In the following example, the `map(_:_:)` operator uses the Swift
|
||||
/// key path syntax to access the `die1` and `die2` members
|
||||
/// of the `DiceRoll` structure published by the `Just` publisher.
|
||||
///
|
||||
/// The downstream sink subscriber receives only
|
||||
/// these two values (as an `(Int, Int)` tuple),
|
||||
/// not the entire `DiceRoll`.
|
||||
///
|
||||
/// struct DiceRoll {
|
||||
/// let die1: Int
|
||||
/// let die2: Int
|
||||
/// }
|
||||
///
|
||||
/// cancellable = Just(DiceRoll(die1: Int.random(in: 1...6),
|
||||
/// die2: Int.random(in: 1...6)))
|
||||
/// .map(\.die1, \.die2)
|
||||
/// .sink { values in
|
||||
/// print("""
|
||||
/// Rolled: \(values.0), \(values.1) \
|
||||
/// (total \(values.0 + values.1))
|
||||
/// """)
|
||||
/// }
|
||||
/// // Prints "Rolled: 5, 3 (total: 8)" (or other random values).
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - keyPath0: The key path of a property on `Output`
|
||||
/// - keyPath1: The key path of another property on `Output`
|
||||
/// - keyPath0: The key path of a property on `Output`.
|
||||
/// - keyPath1: The key path of another property on `Output`.
|
||||
/// - Returns: A publisher that publishes the values of two key paths as a tuple.
|
||||
public func map<Result0, Result1>(
|
||||
_ keyPath0: KeyPath<Output, Result0>,
|
||||
@@ -43,13 +84,38 @@ extension Publisher {
|
||||
keyPath1: keyPath1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a publisher that publishes the values of three key paths as a tuple.
|
||||
/// Publishes the values of three key paths as a tuple.
|
||||
///
|
||||
/// In the following example, the `map(_:_:_:)` operator uses the Swift
|
||||
/// key path syntax to access the `die1`, `die2`, and `die3` members
|
||||
/// of the `DiceRoll` structure published by the `Just` publisher.
|
||||
///
|
||||
/// The downstream sink subscriber receives only
|
||||
/// these three values (as an `(Int, Int, Int)` tuple),
|
||||
/// not the entire `DiceRoll`.
|
||||
///
|
||||
/// struct DiceRoll {
|
||||
/// let die1: Int
|
||||
/// let die2: Int
|
||||
/// let die3: Int
|
||||
/// }
|
||||
///
|
||||
/// cancellable = Just(DiceRoll(die1: Int.random(in: 1...6),
|
||||
/// die2: Int.random(in: 1...6),
|
||||
/// die3: Int.random(in: 1...6)))
|
||||
/// .map(\.die1, \.die2, \.die3)
|
||||
/// .sink { values in
|
||||
/// print("""
|
||||
/// Rolled: \(values.0), \(values.1), \(values.2) \
|
||||
/// (total \(values.0 + values.1 + values.2))
|
||||
/// """)
|
||||
/// }
|
||||
/// // Prints "Rolled: 2, 4, 3 (total: 9)" (or other random values).
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - keyPath0: The key path of a property on `Output`
|
||||
/// - keyPath1: The key path of another property on `Output`
|
||||
/// - keyPath2: The key path of a third property on `Output`
|
||||
/// - keyPath0: The key path of a property on `Output`.
|
||||
/// - keyPath1: The key path of a second property on `Output`.
|
||||
/// - keyPath2: The key path of a third property on `Output`.
|
||||
/// - Returns: A publisher that publishes the values of three key paths as a tuple.
|
||||
public func map<Result0, Result1, Result2>(
|
||||
_ keyPath0: KeyPath<Output, Result0>,
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
|
||||
/// A publisher that emits an output to each subscriber just once, and then finishes.
|
||||
///
|
||||
/// You can use a `Just` publisher to start a chain of publishers. A `Just` publisher
|
||||
/// is also useful when replacing a value with `Catch`.
|
||||
/// You can use a `Just` publisher to start a chain of publishers. A `Just` publisher is
|
||||
/// also useful when replacing a value with `Publishers.Catch`.
|
||||
///
|
||||
/// In contrast with `Publishers.Once`, a `Just` publisher cannot fail with an error.
|
||||
/// In contrast with `Result.Publisher`, a `Just` publisher can’t fail with an error.
|
||||
/// And unlike `Optional.Publisher`, a `Just` publisher always produces a value.
|
||||
public struct Just<Output>: Publisher {
|
||||
|
||||
public typealias Failure = Never
|
||||
@@ -249,6 +250,26 @@ extension Just {
|
||||
) -> Result<ElementOfResult, Error>.OCombine.Publisher {
|
||||
return .init(Result { try nextPartialResult(initialResult, output) })
|
||||
}
|
||||
|
||||
public func prepend(_ elements: Output...) -> Publishers.Sequence<[Output], Never> {
|
||||
return prepend(elements)
|
||||
}
|
||||
|
||||
public func prepend<Elements: Sequence>(
|
||||
_ elements: Elements
|
||||
) -> Publishers.Sequence<[Output], Never> where Output == Elements.Element {
|
||||
return .init(sequence: elements + [output])
|
||||
}
|
||||
|
||||
public func append(_ elements: Output...) -> Publishers.Sequence<[Output], Never> {
|
||||
return append(elements)
|
||||
}
|
||||
|
||||
public func append<Elements: Sequence>(
|
||||
_ elements: Elements
|
||||
) -> Publishers.Sequence<[Output], Never> where Output == Elements.Element {
|
||||
return .init(sequence: [output] + elements)
|
||||
}
|
||||
}
|
||||
|
||||
extension Just {
|
||||
|
||||
@@ -28,28 +28,45 @@ extension Optional {
|
||||
self.optional = optional
|
||||
}
|
||||
|
||||
/// A publisher that publishes an optional value to each subscriber
|
||||
/// exactly once, if the optional has a value.
|
||||
public var publisher: Publisher {
|
||||
return Publisher(optional)
|
||||
}
|
||||
|
||||
/// The type of a Combine publisher that publishes the value of a Swift optional
|
||||
/// instance to each subscriber exactly once, if the instance has any value at
|
||||
/// all.
|
||||
///
|
||||
/// In contrast with `Just`, an `Optional` publisher may send
|
||||
/// no value before completion.
|
||||
/// In contrast with the `Just` publisher, which always produces a single value,
|
||||
/// this publisher might not send any values and instead finish normally,
|
||||
/// if `output` is `nil`.
|
||||
public struct Publisher: OpenCombine.Publisher {
|
||||
|
||||
/// The kind of value published by this publisher.
|
||||
///
|
||||
/// This publisher produces the type wrapped by the optional.
|
||||
public typealias Output = Wrapped
|
||||
|
||||
/// The kind of error this publisher might publish.
|
||||
///
|
||||
/// The optional publisher never produces errors.
|
||||
public typealias Failure = Never
|
||||
|
||||
/// The result to deliver to each subscriber.
|
||||
/// The output to deliver to each subscriber.
|
||||
public let output: Wrapped?
|
||||
|
||||
/// Creates a publisher to emit the optional value of a successful result,
|
||||
/// or fail with an error.
|
||||
/// Creates a publisher to emit the value of the optional, or to finish
|
||||
/// immediately if the optional doesn't have a value.
|
||||
///
|
||||
/// - Parameter result: The result to deliver to each subscriber.
|
||||
/// - Parameter output: The result to deliver to each subscriber.
|
||||
public init(_ output: Output?) {
|
||||
self.output = output
|
||||
}
|
||||
|
||||
/// Implements the Publisher protocol by accepting the subscriber and
|
||||
/// immediately publishing the optional’s value if it has one, or finishing
|
||||
/// normally if it doesn’t.
|
||||
///
|
||||
/// - Parameter subscriber: The subscriber to add.
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Output == Downstream.Input, Failure == Downstream.Failure
|
||||
{
|
||||
@@ -64,13 +81,23 @@ extension Optional {
|
||||
}
|
||||
}
|
||||
|
||||
public var ocombine: OCombine {
|
||||
return .init(self)
|
||||
}
|
||||
|
||||
#if !canImport(Combine)
|
||||
/// A publisher that publishes an optional value to each subscriber
|
||||
/// exactly once, if the optional has a value.
|
||||
/// The type of a Combine publisher that publishes the value of a Swift optional
|
||||
/// instance to each subscriber exactly once, if the instance has any value at
|
||||
/// all.
|
||||
///
|
||||
/// In contrast with `Just`, an `Optional` publisher may send
|
||||
/// no value before completion.
|
||||
/// In contrast with the `Just` publisher, which always produces a single value,
|
||||
/// this publisher might not send any values and instead finish normally,
|
||||
/// if `output` is `nil`.
|
||||
public typealias Publisher = OCombine.Publisher
|
||||
|
||||
public var publisher: Publisher {
|
||||
return Publisher(self)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -235,7 +262,7 @@ extension Optional.OCombine.Publisher {
|
||||
in range: RangeExpression
|
||||
) -> Optional<Output>.OCombine.Publisher where RangeExpression.Bound == Int {
|
||||
let range = range.relative(to: 0 ..< Int.max)
|
||||
precondition(range.lowerBound >= 0, "lowerBould must not be negative")
|
||||
precondition(range.lowerBound >= 0, "lowerBound must not be negative")
|
||||
|
||||
// I don't know why, but Combine has this precondition
|
||||
precondition(range.upperBound < .max - 1)
|
||||
|
||||
@@ -10,13 +10,28 @@ extension Publisher {
|
||||
/// Publishes a single Boolean value that indicates whether all received elements pass
|
||||
/// a given predicate.
|
||||
///
|
||||
/// When this publisher receives an element, it runs the predicate against
|
||||
/// the element. If the predicate returns `false`, the publisher produces a `false`
|
||||
/// value and finishes. If the upstream publisher finishes normally, this publisher
|
||||
/// produces a `true` value and finishes.
|
||||
/// As a `reduce`-style operator, this publisher produces at most one value.
|
||||
/// Backpressure note: Upon receiving any request greater than zero, this publisher
|
||||
/// requests unlimited elements from the upstream publisher.
|
||||
/// Use the `allSatisfy(_:)` operator to determine if all elements in a stream satisfy
|
||||
/// a criteria you provide. When this publisher receives an element, it runs
|
||||
/// the predicate against the element. If the predicate returns `false`, the publisher
|
||||
/// produces a `false` value and finishes. If the upstream publisher finishes
|
||||
/// normally, this publisher produces a `true` value and finishes.
|
||||
///
|
||||
/// In the example below, the `allSatisfy(_:)` operator tests if each an integer array
|
||||
/// publisher’s elements fall into the `targetRange`:
|
||||
///
|
||||
/// let targetRange = (-1...100)
|
||||
/// let numbers = [-1, 0, 10, 5]
|
||||
/// numbers.publisher
|
||||
/// .allSatisfy { targetRange.contains($0) }
|
||||
/// .sink { print("\($0)") }
|
||||
///
|
||||
/// // Prints: "true"
|
||||
///
|
||||
/// With operators similar to `reduce(_:_:)`, this publisher produces at most one
|
||||
/// value.
|
||||
///
|
||||
/// > Note: Upon receiving any request greater than zero, this publisher requests
|
||||
/// unlimited elements from the upstream publisher.
|
||||
///
|
||||
/// - Parameter predicate: A closure that evaluates each received element.
|
||||
/// Return `true` to continue, or `false` to cancel the upstream and complete.
|
||||
@@ -31,21 +46,45 @@ extension Publisher {
|
||||
/// Publishes a single Boolean value that indicates whether all received elements pass
|
||||
/// a given error-throwing predicate.
|
||||
///
|
||||
/// When this publisher receives an element, it runs the predicate against
|
||||
/// the element. If the predicate returns `false`, the publisher produces a `false`
|
||||
/// value and finishes. If the upstream publisher finishes normally, this publisher
|
||||
/// produces a `true` value and finishes. If the predicate throws an error,
|
||||
/// the publisher fails, passing the error to its downstream.
|
||||
/// As a `reduce`-style operator, this publisher produces at most one value.
|
||||
/// Backpressure note: Upon receiving any request greater than zero, this publisher
|
||||
/// requests unlimited elements from the upstream publisher.
|
||||
/// Use the `tryAllSatisfy(_:)` operator to determine if all elements in a stream
|
||||
/// satisfy a criteria in an error-throwing predicate you provide. When this publisher
|
||||
/// receives an element, it runs the predicate against the element. If the predicate
|
||||
/// returns `false`, the publisher produces a `false` value and finishes.
|
||||
/// If the upstream publisher finishes normally, this publisher produces a `true`
|
||||
/// value and finishes. If the predicate throws an error, the publisher fails and
|
||||
/// passes the error to its downstream subscriber.
|
||||
///
|
||||
/// - Parameter predicate: A closure that evaluates each received element.
|
||||
/// Return `true` to continue, or `false` to cancel the upstream and complete.
|
||||
/// The closure may throw, in which case the publisher cancels the upstream
|
||||
/// publisher and fails with the thrown error.
|
||||
/// - Returns: A publisher that publishes a Boolean value that indicates whether
|
||||
/// all received elements pass a given predicate.
|
||||
/// In the example below, an error-throwing predicate tests if each of an integer
|
||||
/// array publisher’s elements fall into the `targetRange`; the predicate throws
|
||||
/// an error if an element is zero and terminates the stream.
|
||||
///
|
||||
/// let targetRange = (-1...100)
|
||||
/// let numbers = [-1, 10, 5, 0]
|
||||
///
|
||||
/// numbers.publisher
|
||||
/// .tryAllSatisfy { anInt in
|
||||
/// guard anInt != 0 else { throw RangeError() }
|
||||
/// return targetRange.contains(anInt)
|
||||
/// }
|
||||
/// .sink(
|
||||
/// receiveCompletion: { print ("completion: \($0)") },
|
||||
/// receiveValue: { print ("value: \($0)") }
|
||||
/// )
|
||||
///
|
||||
/// // Prints: "completion: failure(RangeError())"
|
||||
///
|
||||
/// With operators similar to `reduce(_:_:)`, this publisher produces at most one
|
||||
/// value.
|
||||
///
|
||||
/// > Note: Upon receiving any request greater than zero, this publisher requests
|
||||
/// unlimited elements from the upstream publisher.
|
||||
///
|
||||
/// - Parameter predicate: A closure that evaluates each received element. Return
|
||||
/// `true` to continue, or `false` to cancel the upstream and complete. The closure
|
||||
/// may throw an error, in which case the publisher cancels the upstream publisher
|
||||
/// and fails with the thrown error.
|
||||
/// - Returns: A publisher that publishes a Boolean value that indicates whether all
|
||||
/// received elements pass a given predicate.
|
||||
public func tryAllSatisfy(
|
||||
_ predicate: @escaping (Output) throws -> Bool
|
||||
) -> Publishers.TryAllSatisfy<Self> {
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// Publishers.AssertNoFailure.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 25.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Raises a fatal error when its upstream publisher fails, and otherwise republishes
|
||||
/// all received input.
|
||||
///
|
||||
/// Use `assertNoFailure()` for internal sanity checks that are active during testing.
|
||||
/// However, it is important to note that, like its Swift counterpart
|
||||
/// `fatalError(_:)`, the `assertNoFailure()` operator asserts a fatal exception when
|
||||
/// triggered in both development/testing _and_ shipping versions of code.
|
||||
///
|
||||
/// In the example below, a `CurrentValueSubject` publishes the initial and second
|
||||
/// values successfully. The third value, containing a `genericSubjectError`, causes
|
||||
/// the `assertNoFailure()` operator to assert a fatal exception stopping the process:
|
||||
///
|
||||
/// public enum SubjectError: Error {
|
||||
/// case genericSubjectError
|
||||
/// }
|
||||
///
|
||||
/// let subject = CurrentValueSubject<String, Error>("initial value")
|
||||
/// subject
|
||||
/// .assertNoFailure()
|
||||
/// .sink(receiveCompletion: { print ("completion: \($0)") },
|
||||
/// receiveValue: { print ("value: \($0).") }
|
||||
/// )
|
||||
///
|
||||
/// subject.send("second value")
|
||||
/// subject.send(completion: .failure(SubjectError.genericSubjectError))
|
||||
///
|
||||
/// // Prints:
|
||||
/// // value: initial value.
|
||||
/// // value: second value.
|
||||
/// // The process then terminates in the debugger as the assertNoFailure
|
||||
/// // operator catches the genericSubjectError.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - prefix: A string used at the beginning of the fatal error message.
|
||||
/// - file: A filename used in the error message. This defaults to `#file`.
|
||||
/// - line: A line number used in the error message. This defaults to `#line`.
|
||||
/// - Returns: A publisher that raises a fatal error when its upstream publisher
|
||||
/// fails.
|
||||
public func assertNoFailure(_ prefix: String = "",
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line) -> Publishers.AssertNoFailure<Self> {
|
||||
return .init(upstream: self, prefix: prefix, file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that raises a fatal error upon receiving any failure, and otherwise
|
||||
/// republishes all received input.
|
||||
///
|
||||
/// Use this function for internal sanity checks that are active during testing but
|
||||
/// do not impact performance of shipping code.
|
||||
public struct AssertNoFailure<Upstream: Publisher>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Never
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// The string used at the beginning of the fatal error message.
|
||||
public let prefix: String
|
||||
|
||||
/// The filename used in the error message.
|
||||
public let file: StaticString
|
||||
|
||||
/// The line number used in the error message.
|
||||
public let line: UInt
|
||||
|
||||
public init(upstream: Upstream, prefix: String, file: StaticString, line: UInt) {
|
||||
self.upstream = upstream
|
||||
self.prefix = prefix
|
||||
self.file = file
|
||||
self.line = line
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Output, Downstream.Failure == Never
|
||||
{
|
||||
upstream.subscribe(Inner(downstream: subscriber,
|
||||
prefix: prefix,
|
||||
file: file,
|
||||
line: line))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.AssertNoFailure {
|
||||
private struct Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Upstream.Output, Downstream.Failure == Never
|
||||
{
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
private let prefix: String
|
||||
|
||||
private let file: StaticString
|
||||
|
||||
private let line: UInt
|
||||
|
||||
let combineIdentifier = CombineIdentifier()
|
||||
|
||||
init(downstream: Downstream, prefix: String, file: StaticString, line: UInt) {
|
||||
self.downstream = downstream
|
||||
self.prefix = prefix
|
||||
self.file = file
|
||||
self.line = line
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
downstream.receive(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return downstream.receive(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
downstream.receive(completion: .finished)
|
||||
case .failure(let error):
|
||||
let prefix = self.prefix.isEmpty ? "" : self.prefix + ": "
|
||||
fatalError("\(prefix)\(error)", file: file, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
var description: String { return "AssertNoFailure" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
let children: [Mirror.Child] = [
|
||||
("file", file),
|
||||
("line", line),
|
||||
("prefix", prefix)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,25 @@
|
||||
// Created by Sergej Jaskiewicz on 18/09/2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension ConnectablePublisher {
|
||||
|
||||
/// Automates the process of connecting or disconnecting from this connectable
|
||||
/// publisher.
|
||||
///
|
||||
/// Use `autoconnect()` to simplify working with `ConnectablePublisher` instances,
|
||||
/// such as those created with `makeConnectable()`.
|
||||
/// such as `TimerPublisher` in `OpenCombineFoundation`.
|
||||
///
|
||||
/// let autoconnectedPublisher = somePublisher
|
||||
/// .makeConnectable()
|
||||
/// In the following example, the `Timer.publish()` operator creates
|
||||
/// a `TimerPublisher`, which is a `ConnectablePublisher`. As a result, subscribers
|
||||
/// don’t receive any values until after a call to `connect()`.
|
||||
/// For convenience when working with a single subscriber, the `.autoconnect()`
|
||||
/// operator performs the `connect()` call when attached to by the subscriber.
|
||||
///
|
||||
/// cancellable = Timer.publish(every: 1, on: .main, in: .default)
|
||||
/// .autoconnect()
|
||||
/// .subscribe(someSubscriber)
|
||||
///
|
||||
/// .sink { date in
|
||||
/// print ("Date now: \(date)")
|
||||
/// }
|
||||
/// - Returns: A publisher which automatically connects to its upstream connectable
|
||||
/// publisher.
|
||||
public func autoconnect() -> Publishers.Autoconnect<Self> {
|
||||
@@ -29,12 +33,12 @@ extension ConnectablePublisher {
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that automatically connects and disconnects from this connectable
|
||||
/// publisher.
|
||||
/// A publisher that automatically connects to an upstream connectable publisher.
|
||||
///
|
||||
/// This publisher calls `connect()` on the upstream `ConnectablePublisher` when first
|
||||
/// attached to by a subscriber.
|
||||
public class Autoconnect<Upstream: ConnectablePublisher>: Publisher {
|
||||
|
||||
// NOTE: This class has been audited for thread safety
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// Publishers.Breakpoint.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 03.12.2019.
|
||||
//
|
||||
|
||||
#if !WASI
|
||||
|
||||
#if canImport(COpenCombineHelpers)
|
||||
import COpenCombineHelpers
|
||||
#endif
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Raises a debugger signal when a provided closure needs to stop the process in
|
||||
/// the debugger.
|
||||
///
|
||||
/// Use `breakpoint(receiveSubscription:receiveOutput:receiveCompletion:)` to examine
|
||||
/// one or more stages of the subscribe/publish/completion process and stop in
|
||||
/// the debugger, based on conditions you specify. When any of the provided closures
|
||||
/// returns `true`, this operator raises the `SIGTRAP` signal to stop the process
|
||||
/// in the debugger. Otherwise, this publisher passes through values and completions
|
||||
/// as-is.
|
||||
///
|
||||
/// In the example below, a `PassthroughSubject` publishes strings to a breakpoint
|
||||
/// republisher. When the breakpoint receives the string “`DEBUGGER`”, it returns
|
||||
/// `true`, which stops the app in the debugger.
|
||||
///
|
||||
/// let publisher = PassthroughSubject<String?, Never>()
|
||||
/// cancellable = publisher
|
||||
/// .breakpoint(
|
||||
/// receiveOutput: { value in return value == "DEBUGGER" }
|
||||
/// )
|
||||
/// .sink { print("\(String(describing: $0))" , terminator: " ") }
|
||||
///
|
||||
/// publisher.send("DEBUGGER")
|
||||
///
|
||||
/// // Prints: "error: Execution was interrupted, reason: signal SIGTRAP."
|
||||
/// // Depending on your specific environment, the console messages may
|
||||
/// // also include stack trace information, which is not shown here.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - receiveSubscription: A closure that executes when when the publisher receives
|
||||
/// a subscription. Return `true` from this closure to raise `SIGTRAP`, or `false`
|
||||
/// to continue.
|
||||
/// - receiveOutput: A closure that executes when when the publisher receives
|
||||
/// a value. Return `true` from this closure to raise `SIGTRAP`, or `false`
|
||||
/// to continue.
|
||||
/// - receiveCompletion: A closure that executes when when the publisher receives
|
||||
/// a completion. Return `true` from this closure to raise `SIGTRAP`, or `false`
|
||||
/// to continue.
|
||||
/// - Returns: A publisher that raises a debugger signal when one of the provided
|
||||
/// closures returns `true`.
|
||||
public func breakpoint(
|
||||
receiveSubscription: ((Subscription) -> Bool)? = nil,
|
||||
receiveOutput: ((Output) -> Bool)? = nil,
|
||||
receiveCompletion: ((Subscribers.Completion<Failure>) -> Bool)? = nil
|
||||
) -> Publishers.Breakpoint<Self> {
|
||||
return .init(upstream: self,
|
||||
receiveSubscription: receiveSubscription,
|
||||
receiveOutput: receiveOutput,
|
||||
receiveCompletion: receiveCompletion)
|
||||
}
|
||||
|
||||
/// Raises a debugger signal upon receiving a failure.
|
||||
///
|
||||
/// When the upstream publisher fails with an error, this publisher raises
|
||||
/// the `SIGTRAP` signal, which stops the process in the debugger. Otherwise, this
|
||||
/// publisher passes through values and completions as-is.
|
||||
///
|
||||
/// In this example a `PassthroughSubject` publishes strings, but its downstream
|
||||
/// `Publisher/tryMap(_:)` operator throws an error. This sends the error downstream
|
||||
/// as a `Subscribers.Completion.failure(_:)`. The `breakpointOnError()`
|
||||
/// operator receives this completion and stops the app in the debugger.
|
||||
///
|
||||
/// struct CustomError : Error {}
|
||||
/// let publisher = PassthroughSubject<String?, Error>()
|
||||
/// cancellable = publisher
|
||||
/// .tryMap { stringValue in
|
||||
/// throw CustomError()
|
||||
/// }
|
||||
/// .breakpointOnError()
|
||||
/// .sink(
|
||||
/// receiveCompletion: { completion in
|
||||
/// print("Completion: \(String(describing: completion))")
|
||||
/// },
|
||||
/// receiveValue: { aValue in
|
||||
/// print("Result: \(String(describing: aValue))")
|
||||
/// }
|
||||
/// )
|
||||
///
|
||||
/// publisher.send("TEST DATA")
|
||||
///
|
||||
/// // Prints: "error: Execution was interrupted, reason: signal SIGTRAP."
|
||||
/// // Depending on your specific environment, the console messages may
|
||||
/// // also include stack trace information, which is not shown here.
|
||||
///
|
||||
/// - Returns: A publisher that raises a debugger signal upon receiving a failure.
|
||||
public func breakpointOnError() -> Publishers.Breakpoint<Self> {
|
||||
return breakpoint(receiveCompletion: { completion in
|
||||
switch completion {
|
||||
case .finished:
|
||||
return false
|
||||
case .failure:
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that raises a debugger signal when a provided closure needs to stop
|
||||
/// the process in the debugger.
|
||||
///
|
||||
/// When any of the provided closures returns `true`, this publisher raises
|
||||
/// the `SIGTRAP` signal to stop the process in the debugger.
|
||||
/// Otherwise, this publisher passes through values and completions as-is.
|
||||
public struct Breakpoint<Upstream: Publisher>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// A closure that executes when the publisher receives a subscription, and can
|
||||
/// raise a debugger signal by returning a `true` Boolean value.
|
||||
public let receiveSubscription: ((Subscription) -> Bool)?
|
||||
|
||||
/// A closure that executes when the publisher receives output from the upstream
|
||||
/// publisher, and can raise a debugger signal by returning a `true` Boolean
|
||||
/// value.
|
||||
public let receiveOutput: ((Upstream.Output) -> Bool)?
|
||||
|
||||
/// A closure that executes when the publisher receives completion, and can raise
|
||||
/// a debugger signal by returning a `true` Boolean value.
|
||||
public let receiveCompletion:
|
||||
((Subscribers.Completion<Upstream.Failure>) -> Bool)?
|
||||
|
||||
/// Creates a breakpoint publisher with the provided upstream publisher and
|
||||
/// breakpoint-raising closures.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - upstream: The publisher from which this publisher receives elements.
|
||||
/// - receiveSubscription: A closure that executes when the publisher receives
|
||||
/// a subscription, and can raise a debugger signal by returning a `true`
|
||||
/// Boolean value.
|
||||
/// - receiveOutput: A closure that executes when the publisher receives output
|
||||
/// from the upstream publisher, and can raise a debugger signal by returning
|
||||
/// a `true` Boolean value.
|
||||
/// - receiveCompletion: A closure that executes when the publisher receives
|
||||
/// completion, and can raise a debugger signal by returning a `true` Boolean
|
||||
/// value.
|
||||
public init(
|
||||
upstream: Upstream,
|
||||
receiveSubscription: ((Subscription) -> Bool)? = nil,
|
||||
receiveOutput: ((Upstream.Output) -> Bool)? = nil,
|
||||
receiveCompletion: ((Subscribers.Completion<Failure>) -> Bool)? = nil
|
||||
) {
|
||||
self.upstream = upstream
|
||||
self.receiveSubscription = receiveSubscription
|
||||
self.receiveOutput = receiveOutput
|
||||
self.receiveCompletion = receiveCompletion
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
upstream.subscribe(Inner(self, downstream: subscriber))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.Breakpoint {
|
||||
private struct Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Upstream.Output, Downstream.Failure == Upstream.Failure
|
||||
{
|
||||
typealias Input = Upstream.Output
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
private let downstream: Downstream
|
||||
private let breakpoint: Publishers.Breakpoint<Upstream>
|
||||
|
||||
let combineIdentifier = CombineIdentifier()
|
||||
|
||||
init(_ breakpoint: Publishers.Breakpoint<Upstream>,
|
||||
downstream: Downstream) {
|
||||
self.downstream = downstream
|
||||
self.breakpoint = breakpoint
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
if breakpoint.receiveSubscription?(subscription) == true {
|
||||
__stopInDebugger()
|
||||
}
|
||||
downstream.receive(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
if breakpoint.receiveOutput?(input) == true {
|
||||
__stopInDebugger()
|
||||
}
|
||||
return downstream.receive(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
if breakpoint.receiveCompletion?(completion) == true {
|
||||
__stopInDebugger()
|
||||
}
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return "Breakpoint" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
return Mirror(self, children: EmptyCollection())
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !WASI
|
||||
@@ -0,0 +1,358 @@
|
||||
//
|
||||
// Publishers.Buffer.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 08.01.2020.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Buffers elements received from an upstream publisher.
|
||||
///
|
||||
/// Use `buffer(size:prefetch:whenFull:)` to collect a specific number of elements
|
||||
/// from an upstream publisher before republishing them to the downstream subscriber
|
||||
/// according to the `Publishers.BufferingStrategy` and `Publishers.PrefetchStrategy`
|
||||
/// strategy you specify.
|
||||
///
|
||||
/// If the publisher completes before reaching the `size` threshold, it buffers
|
||||
/// the elements and publishes them downstream prior to completion.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - size: The maximum number of elements to store.
|
||||
/// - prefetch: The strategy to initially populate the buffer.
|
||||
/// - whenFull: The action to take when the buffer becomes full.
|
||||
/// - Returns: A publisher that buffers elements received from an upstream publisher.
|
||||
public func buffer(
|
||||
size: Int,
|
||||
prefetch: Publishers.PrefetchStrategy,
|
||||
whenFull: Publishers.BufferingStrategy<Failure>
|
||||
) -> Publishers.Buffer<Self> {
|
||||
return .init(upstream: self,
|
||||
size: size,
|
||||
prefetch: prefetch,
|
||||
whenFull: whenFull)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A strategy for filling a buffer.
|
||||
public enum PrefetchStrategy {
|
||||
|
||||
/// A strategy to fill the buffer at subscription time, and keep it full
|
||||
/// thereafter.
|
||||
///
|
||||
/// This strategy starts by making a demand equal to the buffer’s size from
|
||||
/// the upstream when the subscriber first connects. Afterwards, it continues
|
||||
/// to demand elements from the upstream to try to keep the buffer full.
|
||||
case keepFull
|
||||
|
||||
/// A strategy that avoids prefetching and instead performs requests on demand.
|
||||
///
|
||||
/// This strategy just forwards the downstream’s requests to the upstream
|
||||
/// publisher.
|
||||
case byRequest
|
||||
}
|
||||
|
||||
/// A strategy that handles exhaustion of a buffer’s capacity.
|
||||
public enum BufferingStrategy<Failure: Error> {
|
||||
|
||||
/// When the buffer is full, discard the newly received element.
|
||||
case dropNewest
|
||||
|
||||
/// When the buffer is full, discard the oldest element in the buffer.
|
||||
case dropOldest
|
||||
|
||||
/// When the buffer is full, execute the closure to provide a custom error.
|
||||
case customError(() -> Failure)
|
||||
}
|
||||
|
||||
/// A publisher that buffers elements received from an upstream publisher.
|
||||
public struct Buffer<Upstream: Publisher>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// The maximum number of elements to store.
|
||||
public let size: Int
|
||||
|
||||
/// The strategy for initially populating the buffer.
|
||||
public let prefetch: Publishers.PrefetchStrategy
|
||||
|
||||
/// The action to take when the buffer becomes full.
|
||||
public let whenFull: Publishers.BufferingStrategy<Failure>
|
||||
|
||||
/// Creates a publisher that buffers elements received from an upstream publisher.
|
||||
/// - Parameter upstream: The publisher from which this publisher receives
|
||||
/// elements.
|
||||
/// - Parameter size: The maximum number of elements to store.
|
||||
/// - Parameter prefetch: The strategy for initially populating the buffer.
|
||||
/// - Parameter whenFull: The action to take when the buffer becomes full.
|
||||
public init(upstream: Upstream,
|
||||
size: Int,
|
||||
prefetch: Publishers.PrefetchStrategy,
|
||||
whenFull: Publishers.BufferingStrategy<Failure>) {
|
||||
self.upstream = upstream
|
||||
self.size = size
|
||||
self.prefetch = prefetch
|
||||
self.whenFull = whenFull
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
let inner = Inner(downstream: subscriber,
|
||||
size: size,
|
||||
prefetch: prefetch,
|
||||
whenFull: whenFull)
|
||||
upstream.subscribe(inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.PrefetchStrategy: Equatable {}
|
||||
|
||||
extension Publishers.PrefetchStrategy: Hashable {}
|
||||
|
||||
extension Publishers.Buffer {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Upstream.Output, Downstream.Failure == Upstream.Failure
|
||||
{
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var recursion = false
|
||||
|
||||
private let size: Int
|
||||
|
||||
private let prefetch: Publishers.PrefetchStrategy // keepFull is 0x0
|
||||
|
||||
private let whenFull: Publishers.BufferingStrategy<Failure>
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
private var state = SubscriptionStatus.awaitingSubscription
|
||||
|
||||
private var downstreamDemand = Subscribers.Demand.none
|
||||
|
||||
// TODO: Use a deque here?
|
||||
// Need to measure performance with large buffers and `dropOldest` strategy.
|
||||
private var values = [Input]()
|
||||
|
||||
private var upstreamFailed = false
|
||||
|
||||
private var terminal: Subscribers.Completion<Failure>?
|
||||
|
||||
init(downstream: Downstream,
|
||||
size: Int,
|
||||
prefetch: Publishers.PrefetchStrategy,
|
||||
whenFull: Publishers.BufferingStrategy<Failure>) {
|
||||
self.size = size
|
||||
self.prefetch = prefetch
|
||||
self.whenFull = whenFull
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .awaitingSubscription = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .subscribed(subscription)
|
||||
lock.unlock()
|
||||
|
||||
let upstreamDemand: Subscribers.Demand
|
||||
switch prefetch {
|
||||
case .keepFull:
|
||||
upstreamDemand = .max(size)
|
||||
case .byRequest:
|
||||
upstreamDemand = .unlimited
|
||||
}
|
||||
subscription.request(upstreamDemand)
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
guard case let .subscribed(subscription) = state else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
switch terminal {
|
||||
case nil, .finished?:
|
||||
if values.count >= size {
|
||||
switch whenFull {
|
||||
case .dropNewest:
|
||||
lock.unlock()
|
||||
return drain()
|
||||
case .dropOldest:
|
||||
values.removeFirst()
|
||||
case let .customError(makeError):
|
||||
terminal = .failure(makeError())
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
values.append(input)
|
||||
lock.unlock()
|
||||
return drain()
|
||||
case .failure?:
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
lock.lock()
|
||||
guard case .subscribed = state, terminal == nil else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
terminal = completion
|
||||
lock.unlock()
|
||||
_ = drain()
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
downstreamDemand += demand
|
||||
let recursion = self.recursion
|
||||
lock.unlock()
|
||||
if recursion {
|
||||
return
|
||||
}
|
||||
|
||||
let more = drain()
|
||||
if more != .none {
|
||||
// Request the number of items just enough to fill the buffer.
|
||||
subscription.request(more)
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard case let .subscribed(subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
values = []
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
private func drain() -> Subscribers.Demand {
|
||||
var upstreamDemand = Subscribers.Demand.none
|
||||
lock.lock()
|
||||
while true {
|
||||
guard case .subscribed = state else {
|
||||
lock.unlock()
|
||||
return upstreamDemand
|
||||
}
|
||||
|
||||
if downstreamDemand > 0 {
|
||||
if values.isEmpty {
|
||||
if let completion = terminal {
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
return upstreamDemand
|
||||
}
|
||||
} else {
|
||||
if let completion = terminal, case .failure = completion {
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
return upstreamDemand
|
||||
}
|
||||
|
||||
let poppedValues = lockedPop(downstreamDemand)
|
||||
assert(poppedValues.count > 0,
|
||||
"""
|
||||
We check that the buffer is not empty and downstreamDemand is \
|
||||
nonzero, how can this be triggered?
|
||||
""")
|
||||
|
||||
// This should not crash because `lockedPop(_:)` returns at most
|
||||
// `downstreamDemand` items.
|
||||
downstreamDemand -= poppedValues.count
|
||||
|
||||
recursion = true
|
||||
lock.unlock()
|
||||
|
||||
var newDownstreamDemand = Subscribers.Demand.none
|
||||
var additionalUpstreamDemand = 0
|
||||
|
||||
for value in poppedValues {
|
||||
newDownstreamDemand += downstream.receive(value)
|
||||
additionalUpstreamDemand += 1
|
||||
}
|
||||
|
||||
if prefetch == .keepFull {
|
||||
upstreamDemand += additionalUpstreamDemand
|
||||
}
|
||||
|
||||
lock.lock()
|
||||
recursion = false
|
||||
downstreamDemand += newDownstreamDemand
|
||||
}
|
||||
}
|
||||
|
||||
private func lockedPop(_ demand: Subscribers.Demand) -> [Input] {
|
||||
assert(demand > 0)
|
||||
guard let max = demand.max else {
|
||||
let poppedValues = self.values
|
||||
self.values = []
|
||||
return poppedValues
|
||||
}
|
||||
|
||||
let poppedValues = Array(values.prefix(max))
|
||||
values.removeFirst(poppedValues.count)
|
||||
return poppedValues
|
||||
}
|
||||
|
||||
var description: String { return "Buffer" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
let children: [Mirror.Child] = [
|
||||
("values", values),
|
||||
("state", state),
|
||||
("downstreamDemand", downstreamDemand),
|
||||
("terminal", terminal as Any)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
${template_header}
|
||||
//
|
||||
// Publishers.Catch.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 25.12.2019.
|
||||
//
|
||||
|
||||
%{
|
||||
instantiations = ['Catch', 'TryCatch']
|
||||
}%
|
||||
extension Publisher {
|
||||
|
||||
/// Handles errors from an upstream publisher by replacing it with another publisher.
|
||||
///
|
||||
/// Use `catch()` to replace an error from an upstream publisher with a new publisher.
|
||||
///
|
||||
/// In the example below, the `catch()` operator handles the `SimpleError` thrown by
|
||||
/// the upstream publisher by replacing the error with a `Just` publisher. This
|
||||
/// continues the stream by publishing a single value and completing normally.
|
||||
///
|
||||
/// struct SimpleError: Error {}
|
||||
/// let numbers = [5, 4, 3, 2, 1, 0, 9, 8, 7, 6]
|
||||
/// cancellable = numbers.publisher
|
||||
/// .tryLast(where: {
|
||||
/// guard $0 != 0 else { throw SimpleError() }
|
||||
/// return true
|
||||
/// })
|
||||
/// .catch { error in
|
||||
/// Just(-1)
|
||||
/// }
|
||||
/// .sink { print("\($0)") }
|
||||
/// // Prints: -1
|
||||
///
|
||||
/// Backpressure note: This publisher passes through `request` and `cancel` to
|
||||
/// the upstream. After receiving an error, the publisher sends sends any unfulfilled
|
||||
/// demand to the new `Publisher`.
|
||||
///
|
||||
/// - SeeAlso: `replaceError`
|
||||
/// - Parameter handler: A closure that accepts the upstream failure as input and
|
||||
/// returns a publisher to replace the upstream publisher.
|
||||
/// - Returns: A publisher that handles errors from an upstream publisher by replacing
|
||||
/// the failed publisher with another publisher.
|
||||
public func `catch`<NewPublisher: Publisher>(
|
||||
_ handler: @escaping (Failure) -> NewPublisher
|
||||
) -> Publishers.Catch<Self, NewPublisher>
|
||||
where NewPublisher.Output == Output
|
||||
{
|
||||
return .init(upstream: self, handler: handler)
|
||||
}
|
||||
|
||||
/// Handles errors from an upstream publisher by either replacing it with another
|
||||
/// publisher or throwing a new error.
|
||||
///
|
||||
/// Use `tryCatch(_:)` to decide how to handle from an upstream publisher by either
|
||||
/// replacing the publisher with a new publisher, or throwing a new error.
|
||||
///
|
||||
/// In the example below, an array publisher emits values that a `tryMap(_:)` operator
|
||||
/// evaluates to ensure the values are greater than zero. If the values aren’t greater
|
||||
/// than zero, the operator throws an error to the downstream subscriber to let it
|
||||
/// know there was a problem. The subscriber, `tryCatch(_:)`, replaces the error with
|
||||
/// a new publisher using ``Just`` to publish a final value before the stream ends
|
||||
/// normally.
|
||||
///
|
||||
/// enum SimpleError: Error { case error }
|
||||
/// var numbers = [5, 4, 3, 2, 1, -1, 7, 8, 9, 10]
|
||||
///
|
||||
/// cancellable = numbers.publisher
|
||||
/// .tryMap { v in
|
||||
/// if v > 0 {
|
||||
/// return v
|
||||
/// } else {
|
||||
/// throw SimpleError.error
|
||||
/// }
|
||||
/// }
|
||||
/// .tryCatch { error in
|
||||
/// Just(0) // Send a final value before completing normally.
|
||||
/// // Alternatively, throw a new error to terminate the stream.
|
||||
/// }
|
||||
/// .sink(receiveCompletion: { print ("Completion: \($0).") },
|
||||
/// receiveValue: { print ("Received \($0).") })
|
||||
/// // Received 5.
|
||||
/// // Received 4.
|
||||
/// // Received 3.
|
||||
/// // Received 2.
|
||||
/// // Received 1.
|
||||
/// // Received 0.
|
||||
/// // Completion: finished.
|
||||
///
|
||||
/// - Parameter handler: A throwing closure that accepts the upstream failure as
|
||||
/// input. This closure can either replace the upstream publisher with a new one,
|
||||
/// or throw a new error to the downstream subscriber.
|
||||
/// - Returns: A publisher that handles errors from an upstream publisher by replacing
|
||||
/// the failed publisher with another publisher, or an error.
|
||||
public func tryCatch<NewPublisher: Publisher>(
|
||||
_ handler: @escaping (Failure) throws -> NewPublisher
|
||||
) -> Publishers.TryCatch<Self, NewPublisher>
|
||||
where NewPublisher.Output == Output
|
||||
{
|
||||
return .init(upstream: self, handler: handler)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that handles errors from an upstream publisher by replacing the failed
|
||||
/// publisher with another publisher.
|
||||
public struct Catch<Upstream: Publisher, NewPublisher: Publisher>: Publisher
|
||||
where Upstream.Output == NewPublisher.Output
|
||||
{
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = NewPublisher.Failure
|
||||
|
||||
/// The publisher that this publisher receives elements from.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// A closure that accepts the upstream failure as input and returns a publisher
|
||||
/// to replace the upstream publisher.
|
||||
public let handler: (Upstream.Failure) -> NewPublisher
|
||||
|
||||
/// Creates a publisher that handles errors from an upstream publisher by
|
||||
/// replacing the failed publisher with another publisher.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - upstream: The publisher that this publisher receives elements from.
|
||||
/// - handler: A closure that accepts the upstream failure as input and returns
|
||||
/// a publisher to replace the upstream publisher.
|
||||
public init(upstream: Upstream,
|
||||
handler: @escaping (Upstream.Failure) -> NewPublisher) {
|
||||
self.upstream = upstream
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
let inner = Inner(downstream: subscriber, handler: handler)
|
||||
let uncaughtS = Inner.UncaughtS(inner: inner)
|
||||
upstream.subscribe(uncaughtS)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher that handles errors from an upstream publisher by replacing
|
||||
/// the failed publisher with another publisher or producing a new error.
|
||||
///
|
||||
/// Because this publisher’s handler can throw an error, `Publishers.TryCatch` defines
|
||||
/// its `Failure` type as `Error`. This is different from `Publishers.Catch`, which
|
||||
/// gets its failure type from the replacement publisher.
|
||||
public struct TryCatch<Upstream: Publisher, NewPublisher: Publisher>: Publisher
|
||||
where Upstream.Output == NewPublisher.Output
|
||||
{
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Error
|
||||
|
||||
/// The publisher that this publisher receives elements from.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// A closure that accepts the upstream failure as input and either returns
|
||||
/// a publisher to replace the upstream publisher or throws an error.
|
||||
public let handler: (Upstream.Failure) throws -> NewPublisher
|
||||
|
||||
/// Creates a publisher that handles errors from an upstream publisher by
|
||||
/// replacing the failed publisher with another publisher or by throwing an error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - upstream: The publisher that this publisher receives elements from.
|
||||
/// - handler: A closure that accepts the upstream failure as input and either
|
||||
/// returns a publisher to replace the upstream publisher. If this closure
|
||||
/// throws an error, the publisher terminates with the thrown error.
|
||||
public init(upstream: Upstream,
|
||||
handler: @escaping (Upstream.Failure) throws -> NewPublisher) {
|
||||
self.upstream = upstream
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Output, Downstream.Failure == Failure
|
||||
{
|
||||
let inner = Inner(downstream: subscriber, handler: handler)
|
||||
let uncaughtS = Inner.UncaughtS(inner: inner)
|
||||
upstream.subscribe(uncaughtS)
|
||||
}
|
||||
}
|
||||
}
|
||||
% for instantiation in instantiations:
|
||||
% throws_modifier = ' throws' if instantiation == 'TryCatch' else ''
|
||||
|
||||
extension Publishers.${instantiation} {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Upstream.Output,
|
||||
% if instantiation == 'Catch':
|
||||
Downstream.Failure == NewPublisher.Failure
|
||||
% else:
|
||||
Downstream.Failure == Error
|
||||
% end
|
||||
{
|
||||
struct UncaughtS: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
let inner: Inner
|
||||
|
||||
var combineIdentifier: CombineIdentifier { return inner.combineIdentifier }
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
inner.receivePre(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return inner.receivePre(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
return inner.receivePre(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return inner.description }
|
||||
|
||||
var customMirror: Mirror { return inner.customMirror }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
|
||||
struct CaughtS: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
typealias Input = NewPublisher.Output
|
||||
|
||||
typealias Failure = NewPublisher.Failure
|
||||
|
||||
let inner: Inner
|
||||
|
||||
var combineIdentifier: CombineIdentifier { return inner.combineIdentifier }
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
inner.receivePost(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return inner.receivePost(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
inner.receivePost(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return inner.description }
|
||||
|
||||
var customMirror: Mirror { return inner.customMirror }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
|
||||
private enum State {
|
||||
case pendingPre
|
||||
case pre(Subscription)
|
||||
case pendingPost
|
||||
case post(Subscription)
|
||||
case cancelled
|
||||
}
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var demand = Subscribers.Demand.none
|
||||
|
||||
private var state = State.pendingPre
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
private let handler: (Upstream.Failure)${throws_modifier} -> NewPublisher
|
||||
|
||||
init(downstream: Downstream,
|
||||
handler: @escaping (Upstream.Failure)${throws_modifier} -> NewPublisher) {
|
||||
self.downstream = downstream
|
||||
self.handler = handler
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receivePre(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .pendingPre = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .pre(subscription)
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receivePre(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
demand -= 1
|
||||
lock.unlock()
|
||||
let newDemand = downstream.receive(input)
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
return newDemand
|
||||
}
|
||||
|
||||
func receivePre(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
switch completion {
|
||||
case .finished:
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pre:
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
downstream.receive(completion: .finished)
|
||||
case .pendingPre, .pendingPost, .post, .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
case .failure(let error):
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pre:
|
||||
state = .pendingPost
|
||||
lock.unlock()
|
||||
% if instantiation == 'Catch':
|
||||
handler(error).subscribe(CaughtS(inner: self))
|
||||
% else:
|
||||
do {
|
||||
try handler(error).subscribe(CaughtS(inner: self))
|
||||
} catch let anotherError {
|
||||
lock.lock()
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
downstream.receive(completion: .failure(anotherError))
|
||||
}
|
||||
% end
|
||||
case .cancelled:
|
||||
lock.unlock()
|
||||
case .pendingPre, .post, .pendingPost:
|
||||
completionBeforeSubscription()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receivePost(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .pendingPost = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .post(subscription)
|
||||
let demand = self.demand
|
||||
lock.unlock()
|
||||
if demand > 0 {
|
||||
subscription.request(demand)
|
||||
}
|
||||
}
|
||||
|
||||
func receivePost(_ input: NewPublisher.Output) -> Subscribers.Demand {
|
||||
return downstream.receive(input)
|
||||
}
|
||||
|
||||
func receivePost(completion: Subscribers.Completion<NewPublisher.Failure>) {
|
||||
lock.lock()
|
||||
guard case .post = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
% if instantiation == 'Catch':
|
||||
downstream.receive(completion: completion)
|
||||
% else:
|
||||
downstream.receive(completion: completion.eraseError())
|
||||
% end
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
switch state {
|
||||
case .pendingPre:
|
||||
// The client is only able to call the `request` method after we've sent
|
||||
// `self` downstream. We only do it in the `receivePre(subscription:)`
|
||||
// method, after setting `state` to `pre`.
|
||||
// After that `state` never becomes `pendingPre`.
|
||||
requestBeforeSubscription()
|
||||
case let .pre(subscription):
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
case .pendingPost:
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
case let .post(subscription):
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
case .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
switch state {
|
||||
case let .pre(subscription), let .post(subscription):
|
||||
state = .cancelled
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
case .pendingPre, .pendingPost, .cancelled:
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
var description: String { return "${instantiation}" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
let children: [Mirror.Child] = [
|
||||
("downstream", downstream),
|
||||
("demand", demand)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
% end
|
||||
|
||||
private func completionBeforeSubscription(file: StaticString = #file,
|
||||
line: UInt = #line) -> Never {
|
||||
fatalError("Unexpected state: received completion but do not have subscription",
|
||||
file: file,
|
||||
line: line)
|
||||
}
|
||||
|
||||
private func requestBeforeSubscription(file: StaticString = #file,
|
||||
line: UInt = #line) -> Never {
|
||||
fatalError("Unexpected state: request before subscription sent",
|
||||
file: file,
|
||||
line: line)
|
||||
}
|
||||
@@ -10,13 +10,30 @@ extension Publisher {
|
||||
/// Collects all received elements, and emits a single array of the collection when
|
||||
/// the upstream publisher finishes.
|
||||
///
|
||||
/// Use `collect()` to gather elements into an array that the operator emits after
|
||||
/// the upstream publisher finishes.
|
||||
///
|
||||
/// If the upstream publisher fails with an error, this publisher forwards the error
|
||||
/// to the downstream receiver instead of sending its output.
|
||||
///
|
||||
/// This publisher requests an unlimited number of elements from the upstream
|
||||
/// publisher. It only sends the collected array to its downstream after a request
|
||||
/// whose demand is greater than 0 items.
|
||||
/// Note: This publisher uses an unbounded amount of memory to store the received
|
||||
/// values.
|
||||
/// publisher and uses an unbounded amount of memory to store the received values.
|
||||
/// The publisher may exert memory pressure on the system for very large sets of
|
||||
/// elements.
|
||||
///
|
||||
/// The `collect()` operator only sends the collected array to its downstream receiver
|
||||
/// after a request whose demand is greater than 0 items. Otherwise, `collect()` waits
|
||||
/// until it receives a non-zero request.
|
||||
///
|
||||
/// In the example below, an Integer range is a publisher that emits an array of
|
||||
/// integers:
|
||||
///
|
||||
/// let numbers = (0...10)
|
||||
/// cancellable = numbers.publisher
|
||||
/// .collect()
|
||||
/// .sink { print("\($0)") }
|
||||
///
|
||||
/// // Prints: "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
|
||||
///
|
||||
/// - Returns: A publisher that collects all received items and returns them as
|
||||
/// an array upon completion.
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// Publishers.CollectByCount.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 24.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Collects up to the specified number of elements, and then emits a single array of
|
||||
/// the collection.
|
||||
///
|
||||
/// Use `collect(_:)` to emit arrays of at most `count` elements from an upstream
|
||||
/// publisher. If the upstream publisher finishes before collecting the specified
|
||||
/// number of elements, the publisher sends an array of only the items it received
|
||||
/// This may be fewer than `count` elements.
|
||||
///
|
||||
/// If the upstream publisher fails with an error, this publisher forwards the error
|
||||
/// to the downstream receiver instead of sending its output.
|
||||
///
|
||||
/// In the example below, the `collect(_:)` operator emits one partial and two full
|
||||
/// arrays based on the requested collection size of `5`:
|
||||
///
|
||||
/// let numbers = (0...10)
|
||||
/// cancellable = numbers.publisher
|
||||
/// .collect(5)
|
||||
/// .sink { print("\($0), terminator: " "") }
|
||||
///
|
||||
/// // Prints "[0, 1, 2, 3, 4] [5, 6, 7, 8, 9] [10] "
|
||||
///
|
||||
/// > Note: When this publisher receives a request for `.max(n)` elements, it requests
|
||||
/// `.max(count * n)` from the upstream publisher.
|
||||
///
|
||||
/// - Parameter count: The maximum number of received elements to buffer before
|
||||
/// publishing.
|
||||
/// - Returns: A publisher that collects up to the specified number of elements, and
|
||||
/// then publishes them as an array.
|
||||
public func collect(_ count: Int) -> Publishers.CollectByCount<Self> {
|
||||
return .init(upstream: self, count: count)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that buffers a maximum number of items.
|
||||
public struct CollectByCount<Upstream: Publisher>: Publisher {
|
||||
|
||||
public typealias Output = [Upstream.Output]
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// The maximum number of received elements to buffer before publishing.
|
||||
public let count: Int
|
||||
|
||||
public init(upstream: Upstream, count: Int) {
|
||||
self.upstream = upstream
|
||||
self.count = count
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Failure == Failure, Downstream.Input == Output
|
||||
{
|
||||
upstream.subscribe(Inner(downstream: subscriber, count: count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.CollectByCount: Equatable where Upstream: Equatable {}
|
||||
|
||||
extension Publishers.CollectByCount {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == [Upstream.Output],
|
||||
Downstream.Failure == Upstream.Failure
|
||||
{
|
||||
typealias Input = Upstream.Output
|
||||
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
private let count: Int
|
||||
|
||||
private var buffer: [Input] = []
|
||||
|
||||
private var subscription: Subscription?
|
||||
|
||||
private var finished = false
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
init(downstream: Downstream, count: Int) {
|
||||
self.downstream = downstream
|
||||
self.count = count
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
if finished || self.subscription != nil {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
self.subscription = subscription
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receive(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
if subscription == nil {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
buffer.append(input)
|
||||
guard buffer.count == count else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
let output = self.buffer
|
||||
self.buffer = []
|
||||
lock.unlock()
|
||||
return downstream.receive(output) * count
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
lock.lock()
|
||||
subscription = nil
|
||||
finished = true
|
||||
switch completion {
|
||||
case .finished:
|
||||
if buffer.isEmpty {
|
||||
lock.unlock()
|
||||
} else {
|
||||
let buffer = self.buffer
|
||||
self.buffer = []
|
||||
lock.unlock()
|
||||
_ = downstream.receive(buffer)
|
||||
}
|
||||
case .failure:
|
||||
buffer = []
|
||||
lock.unlock()
|
||||
}
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
if let subscription = self.subscription {
|
||||
lock.unlock()
|
||||
subscription.request(demand * count)
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
if let subscription = self.subscription {
|
||||
buffer = []
|
||||
finished = true
|
||||
self.subscription = nil
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
var description: String { return "CollectByCount" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("downstream", downstream),
|
||||
("upstreamSubscription", subscription as Any),
|
||||
("buffer", buffer),
|
||||
("count", count)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
${template_header}
|
||||
//
|
||||
// Publishers.CombineLatest.swift.gyb
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
%{
|
||||
from gyb_opencombine_support import (
|
||||
suffix_variadic,
|
||||
list_with_suffix_variadic,
|
||||
indent
|
||||
)
|
||||
|
||||
import string
|
||||
|
||||
instantiations = [(2, 'two', 'A second'),
|
||||
(3, 'three', 'A third'),
|
||||
(4, 'four', 'A fourth')]
|
||||
|
||||
def make_publisher_name(arity):
|
||||
return suffix_variadic('CombineLatest', arity, arity - 1)
|
||||
|
||||
def make_upstream_types(arity, start=0):
|
||||
return [str(c) for c in string.ascii_uppercase[start:(start + arity)]]
|
||||
|
||||
def make_upstream_generic_constraints(upstream_types, first_is_self=False):
|
||||
|
||||
format_string = '{0}Failure == {1}.Failure'
|
||||
|
||||
def format(i):
|
||||
return format_string.format(upstream_types[i] + '.',
|
||||
upstream_types[i + 1])
|
||||
|
||||
result = [format(i) for i in range(len(upstream_types) - 1)]
|
||||
|
||||
if first_is_self:
|
||||
result.insert(0, format_string.format('', upstream_types[0]))
|
||||
|
||||
return result
|
||||
|
||||
def declare_combine_latest_method(arity, transform):
|
||||
arg_count = arity - 1
|
||||
declaration_format = """\
|
||||
public func combineLatest<{}>(
|
||||
{}
|
||||
) -> {}
|
||||
where {}\
|
||||
"""
|
||||
upstream_types = make_upstream_types(arg_count, 15)
|
||||
method_generic_params = \
|
||||
[upstream_type + ': Publisher' for upstream_type in upstream_types]
|
||||
if transform:
|
||||
method_generic_params.append('Result')
|
||||
cs_method_generic_params = ', '.join(method_generic_params)
|
||||
method_args = ['_ other: P'] if arg_count == 1 \
|
||||
else ['_ publisher{}: {}'.format(i + 1, upstream_types[i]) \
|
||||
for i in range(arg_count)]
|
||||
if transform:
|
||||
output_types = ['Output'] + ['{}.Output'.format(upstream_type) \
|
||||
for upstream_type in upstream_types]
|
||||
cs_output_types = ', '.join(output_types)
|
||||
method_args \
|
||||
.append('_ transform: @escaping ({}) -> Result'.format(cs_output_types))
|
||||
cs_method_args = ',\n '.join(method_args)
|
||||
|
||||
publisher_generic_params = ['Self'] + upstream_types
|
||||
|
||||
cs_publisher_generic_params = ', '.join(publisher_generic_params)
|
||||
|
||||
publisher_name = 'Publishers.{}<{}>'.format(make_publisher_name(arity),
|
||||
cs_publisher_generic_params)
|
||||
|
||||
if transform:
|
||||
publisher_name = 'Publishers.Map<{}, Result>'.format(publisher_name)
|
||||
|
||||
generic_constraints = make_upstream_generic_constraints(upstream_types,
|
||||
first_is_self=True)
|
||||
|
||||
cs_generic_constraints = ',\n '.join(generic_constraints)
|
||||
|
||||
declaration = declaration_format.format(cs_method_generic_params,
|
||||
cs_method_args,
|
||||
publisher_name,
|
||||
cs_generic_constraints)
|
||||
|
||||
return indent(declaration, 4)
|
||||
}%
|
||||
|
||||
// swiftlint:disable generic_type_name
|
||||
// swiftlint:disable large_tuple
|
||||
|
||||
// MARK: - CombineLatest methods on Publisher
|
||||
|
||||
extension Publisher {
|
||||
|
||||
% for arity, _, _ in instantiations:
|
||||
%
|
||||
% argument_names = ['other'] \
|
||||
% if arity == 2 else ['publisher{}'.format(i) for i in range(1, arity)]
|
||||
% doc_cardinal = 'an additional publisher' if arity == 2 \
|
||||
% else '{} additional publishers'.format(instantiations[arity - 3][1])
|
||||
/// Subscribes to ${doc_cardinal} and publishes a tuple upon
|
||||
/// receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
% for i in range(arity - 1):
|
||||
% param_doc = 'Another' if arity == 2 else instantiations[i][2]
|
||||
/// - ${argument_names[i]}: ${param_doc} publisher to combine with this one.
|
||||
% end
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
${declare_combine_latest_method(arity, transform=False)}
|
||||
{
|
||||
return .init(self, ${', '.join(argument_names)})
|
||||
}
|
||||
|
||||
/// Subscribes to ${doc_cardinal} and invokes a closure
|
||||
/// upon receiving output from either publisher.
|
||||
///
|
||||
/// The combined publisher passes through any requests to *all* upstream publishers.
|
||||
/// However, it still obeys the demand-fulfilling rule of only sending the request
|
||||
/// amount downstream. If the demand isn’t `.unlimited`, it drops values from upstream
|
||||
/// publishers. It implements this by using a buffer size of 1 for each upstream, and
|
||||
/// holds the most recent value in each buffer.
|
||||
/// All upstream publishers need to finish for this publisher to finsh. If an upstream
|
||||
/// publisher never publishes a value, this publisher never finishes.
|
||||
/// If any of the combined publishers terminates with a failure, this publisher also
|
||||
/// fails.
|
||||
///
|
||||
/// - Parameters:
|
||||
% for i in range(arity - 1):
|
||||
% param_doc = 'Another' if arity == 2 else instantiations[i][2]
|
||||
/// - ${argument_names[i]}: ${param_doc} publisher to combine with this one.
|
||||
% end
|
||||
/// - transform: A closure that receives the most recent value from each publisher
|
||||
/// and returns a new value to publish.
|
||||
/// - Returns: A publisher that receives and combines elements from this and another
|
||||
/// publisher.
|
||||
${declare_combine_latest_method(arity, transform=True)}
|
||||
{
|
||||
% publisher_name = make_publisher_name(arity)
|
||||
return Publishers.${publisher_name}(self, ${', '.join(argument_names)}).map {
|
||||
transform(${', '.join(['${}'.format(i) for i in range(arity)])})
|
||||
}
|
||||
}
|
||||
% end
|
||||
}
|
||||
|
||||
// MARK: - CombineLatest publishers
|
||||
|
||||
extension Publishers {
|
||||
% for arity, cardinal, _ in instantiations:
|
||||
% publisher_name = make_publisher_name(arity)
|
||||
% upstream_types = make_upstream_types(arity)
|
||||
%
|
||||
% upstream_generic_params = \
|
||||
% [upstream_type + ': Publisher' for upstream_type in upstream_types]
|
||||
%
|
||||
% cs_upstream_generic_params = ', '.join(upstream_generic_params)
|
||||
%
|
||||
% output_types = [upstream_type + '.Output' for upstream_type in upstream_types]
|
||||
%
|
||||
% cs_output_types = ', '.join(output_types)
|
||||
%
|
||||
% upstream_generic_constraints = \
|
||||
% make_upstream_generic_constraints(upstream_types)
|
||||
%
|
||||
% cs_upstream_generic_constraints = \
|
||||
% ',\n '.join(upstream_generic_constraints)
|
||||
%
|
||||
% init_args = ['_ {}: {}'.format(upstream_type.lower(), upstream_type) \
|
||||
% for upstream_type in upstream_types]
|
||||
% cs_init_args = ',\n '.join(init_args)
|
||||
%
|
||||
% self_fields = [upstream_type.lower() for upstream_type in upstream_types]
|
||||
|
||||
/// A publisher that receives and combines the latest elements from ${cardinal}
|
||||
/// publishers.
|
||||
public struct ${publisher_name}<${cs_upstream_generic_params}>
|
||||
: Publisher
|
||||
where ${cs_upstream_generic_constraints}
|
||||
{
|
||||
public typealias Output = (${cs_output_types})
|
||||
|
||||
public typealias Failure = ${upstream_types[0]}.Failure
|
||||
% for upstream_type in upstream_types:
|
||||
|
||||
public let ${upstream_type.lower()}: ${upstream_type}
|
||||
% end
|
||||
|
||||
public init(
|
||||
${cs_init_args}
|
||||
) {
|
||||
% for self_field in self_fields:
|
||||
self.${self_field} = ${self_field}
|
||||
% end
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Failure == Failure,
|
||||
Downstream.Input == Output
|
||||
{
|
||||
% cs_indented_output_types = (',\n' + (50 * ' ')).join(output_types)
|
||||
typealias Inner = CombineLatest${arity}Inner<${cs_indented_output_types},
|
||||
Failure,
|
||||
Downstream>
|
||||
let inner = Inner(downstream: subscriber, upstreamCount: ${arity})
|
||||
% for i in range(arity):
|
||||
${self_fields[i]}.subscribe(Inner.Side(index: ${i}, combiner: inner))
|
||||
% end
|
||||
subscriber.receive(subscription: inner)
|
||||
}
|
||||
}
|
||||
% end
|
||||
}
|
||||
|
||||
// MARK: - Equatable conformances
|
||||
% for arity, _, _ in instantiations:
|
||||
%
|
||||
% publisher_name = make_publisher_name(arity)
|
||||
%
|
||||
% upstream_types = make_upstream_types(arity)
|
||||
%
|
||||
% constraints = [upstream_type + ': Equatable' for upstream_type in upstream_types]
|
||||
% cs_constraints = ',\n'.join(constraints)
|
||||
% cs_constraints = indent(cs_constraints, 8)
|
||||
%
|
||||
|
||||
extension Publishers.${publisher_name}: Equatable
|
||||
where
|
||||
${cs_constraints} {}
|
||||
% end
|
||||
|
||||
// MARK: - Inners
|
||||
% for arity, _, _ in instantiations:
|
||||
%
|
||||
% publisher_name = make_publisher_name(arity)
|
||||
%
|
||||
% upstream_types = make_upstream_types(arity)
|
||||
%
|
||||
% input_types = ['Input{}'.format(i) for i in range(arity)]
|
||||
%
|
||||
% converters = ['values[{}] as! {}'.format(i, input_types[i]) for i in range(arity)]
|
||||
% output_type = '({})'.format(', '.join(input_types))
|
||||
|
||||
private final class CombineLatest${arity}Inner<${(',\n' + (40 * ' ')).join(input_types)},
|
||||
Failure,
|
||||
Downstream: Subscriber>
|
||||
: AbstractCombineLatest<${output_type}, Failure, Downstream>
|
||||
where Downstream.Input == ${output_type},
|
||||
Downstream.Failure == Failure
|
||||
{
|
||||
override func convert(values: [Any?]) -> (${', '.join(input_types)}) {
|
||||
return (${',\n '.join(converters)})
|
||||
}
|
||||
}
|
||||
% end
|
||||
@@ -7,29 +7,79 @@
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Calls a closure with each received element and publishes any returned
|
||||
/// optional that has a value.
|
||||
/// Calls a closure with each received element and publishes any returned optional
|
||||
/// that has a value.
|
||||
///
|
||||
/// - Parameter transform: A closure that receives a value and returns
|
||||
/// an optional value.
|
||||
/// - Returns: A publisher that republishes all non-`nil` results of calling
|
||||
/// the transform closure.
|
||||
/// OpenCombine’s `compactMap(_:)` operator performs a function similar to that of
|
||||
/// `compactMap(_:)` in the Swift standard library: the `compactMap(_:)` operator in
|
||||
/// OpenCombine removes `nil` elements in a publisher’s stream and republishes
|
||||
/// non-`nil` elements to the downstream subscriber.
|
||||
///
|
||||
/// The example below uses a range of numbers as the source for a collection based
|
||||
/// publisher. The `compactMap(_:)` operator consumes each element from the `numbers`
|
||||
/// publisher attempting to access the dictionary using the element as the key.
|
||||
/// If the example’s dictionary returns a `nil`, due to a non-existent key,
|
||||
/// `compactMap(_:)` filters out the `nil` (missing) elements.
|
||||
///
|
||||
/// let numbers = (0...5)
|
||||
/// let romanNumeralDict: [Int : String] =
|
||||
/// [1: "I", 2: "II", 3: "III", 5: "V"]
|
||||
///
|
||||
/// cancellable = numbers.publisher
|
||||
/// .compactMap { romanNumeralDict[$0] }
|
||||
/// .sink { print("\($0)", terminator: " ") }
|
||||
///
|
||||
/// // Prints: "I II III V"
|
||||
///
|
||||
/// - Parameter transform: A closure that receives a value and returns an optional
|
||||
/// value.
|
||||
/// - Returns: Any non-`nil` optional results of the calling the supplied closure.
|
||||
public func compactMap<ElementOfResult>(
|
||||
_ transform: @escaping (Output) -> ElementOfResult?
|
||||
) -> Publishers.CompactMap<Self, ElementOfResult> {
|
||||
return .init(upstream: self, transform: transform)
|
||||
}
|
||||
|
||||
/// Calls an error-throwing closure with each received element and publishes
|
||||
/// any returned optional that has a value.
|
||||
/// Calls an error-throwing closure with each received element and publishes any
|
||||
/// returned optional that has a value.
|
||||
///
|
||||
/// If the closure throws an error, the publisher cancels the upstream and sends
|
||||
/// the thrown error to the downstream receiver as a `Failure`.
|
||||
/// Use `tryCompactMap(_:)` to remove `nil` elements from a publisher’s stream based
|
||||
/// on an error-throwing closure you provide. If the closure throws an error,
|
||||
/// the publisher cancels the upstream publisher and sends the thrown error to
|
||||
/// the downstream subscriber as a `Publisher.Failure`.
|
||||
///
|
||||
/// - Parameter transform: an error-throwing closure that receives a value
|
||||
/// and returns an optional value.
|
||||
/// - Returns: A publisher that republishes all non-`nil` results of calling
|
||||
/// the `transform` closure.
|
||||
/// The following example uses an array of numbers as the source for
|
||||
/// a collection-based publisher. A `tryCompactMap(_:)` operator consumes each integer
|
||||
/// from the publisher and uses a dictionary to transform the numbers from its Arabic
|
||||
/// to Roman numerals, as an optional `String`.
|
||||
///
|
||||
/// If the closure called by `tryCompactMap(_:)` fails to look up a Roman numeral,
|
||||
/// it returns the optional String `(unknown)`.
|
||||
///
|
||||
/// If the closure called by `tryCompactMap(_:)` determines the input is `0`, it
|
||||
/// throws an error. The `tryCompactMap(_:)` operator catches this error and stops
|
||||
/// publishing, sending a `Subscribers.Completion.failure(_:)` that wraps the error.
|
||||
///
|
||||
/// struct ParseError: Error {}
|
||||
/// func romanNumeral(from: Int) throws -> String? {
|
||||
/// let romanNumeralDict: [Int : String] =
|
||||
/// [1: "I", 2: "II", 3: "III", 4: "IV", 5: "V"]
|
||||
/// guard from != 0 else { throw ParseError() }
|
||||
/// return romanNumeralDict[from]
|
||||
/// }
|
||||
/// let numbers = [6, 5, 4, 3, 2, 1, 0]
|
||||
/// cancellable = numbers.publisher
|
||||
/// .tryCompactMap { try romanNumeral(from: $0) }
|
||||
/// .sink(
|
||||
/// receiveCompletion: { print ("\($0)") },
|
||||
/// receiveValue: { print ("\($0)", terminator: " ") }
|
||||
/// )
|
||||
///
|
||||
/// // Prints: "(Unknown) V IV III II I failure(ParseError())"
|
||||
///
|
||||
/// - Parameter transform: An error-throwing closure that receives a value and returns
|
||||
/// an optional value.
|
||||
/// - Returns: Any non-`nil` optional results of calling the supplied closure.
|
||||
public func tryCompactMap<ElementOfResult>(
|
||||
_ transform: @escaping (Output) throws -> ElementOfResult?
|
||||
) -> Publishers.TryCompactMap<Self, ElementOfResult> {
|
||||
@@ -122,23 +172,48 @@ extension Publishers {
|
||||
}
|
||||
|
||||
extension Publishers.CompactMap {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: FilterProducer<Downstream,
|
||||
Upstream.Output,
|
||||
Output,
|
||||
Upstream.Failure,
|
||||
(Upstream.Output) -> Output?>
|
||||
where Downstream.Failure == Upstream.Failure, Downstream.Input == Output
|
||||
private struct Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Upstream.Failure == Downstream.Failure
|
||||
{
|
||||
// NOTE: This class has been audited for thread safety
|
||||
typealias Input = Upstream.Output
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
override func receive(
|
||||
newValue: Upstream.Output
|
||||
) -> PartialCompletion<Output?, Downstream.Failure> {
|
||||
return .continue(filter(newValue))
|
||||
private let downstream: Downstream
|
||||
private let filter: (Input) -> Downstream.Input?
|
||||
|
||||
let combineIdentifier = CombineIdentifier()
|
||||
|
||||
init(downstream: Downstream, filter: @escaping (Input) -> Downstream.Input?) {
|
||||
self.downstream = downstream
|
||||
self.filter = filter
|
||||
}
|
||||
|
||||
override var description: String { return "CompactMap" }
|
||||
func receive(subscription: Subscription) {
|
||||
downstream.receive(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
if let output = filter(input) {
|
||||
return downstream.receive(output)
|
||||
}
|
||||
return .max(1)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return "CompactMap" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
return Mirror(self, children: EmptyCollection())
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +226,6 @@ extension Publishers.TryCompactMap {
|
||||
(Upstream.Output) throws -> Output?>
|
||||
where Downstream.Failure == Error, Downstream.Input == Output
|
||||
{
|
||||
// NOTE: This class has been audited for thread safety
|
||||
|
||||
override func receive(
|
||||
newValue: Upstream.Output
|
||||
) -> PartialCompletion<Output?, Error> {
|
||||
|
||||
@@ -10,6 +10,20 @@ extension Publisher where Output: Comparable {
|
||||
/// Publishes the minimum value received from the upstream publisher, after it
|
||||
/// finishes.
|
||||
///
|
||||
/// Use `min()` to find the minimum value in a stream of elements from
|
||||
/// an upstream publisher.
|
||||
///
|
||||
/// In the example below, the `min()` operator emits a value when the publisher
|
||||
/// finishes, that value is the minimum of the values received from upstream, which
|
||||
/// is `-1`.
|
||||
///
|
||||
/// let numbers = [-1, 0, 10, 5]
|
||||
/// numbers.publisher
|
||||
/// .min()
|
||||
/// .sink { print("\($0)") }
|
||||
///
|
||||
/// // Prints: "-1"
|
||||
///
|
||||
/// After this publisher receives a request for more than 0 items, it requests
|
||||
/// unlimited items from its upstream publisher.
|
||||
///
|
||||
@@ -22,6 +36,20 @@ extension Publisher where Output: Comparable {
|
||||
/// Publishes the maximum value received from the upstream publisher, after it
|
||||
/// finishes.
|
||||
///
|
||||
/// Use `max()` to determine the maximum value in the stream of elements from
|
||||
/// an upstream publisher.
|
||||
///
|
||||
/// In the example below, the `max()` operator emits a value when the publisher
|
||||
/// finishes, that value is the maximum of the values received from upstream, which
|
||||
/// is `10`.
|
||||
///
|
||||
/// let numbers = [0, 10, 5]
|
||||
/// cancellable = numbers.publisher
|
||||
/// .max()
|
||||
/// .sink { print("\($0)") }
|
||||
///
|
||||
/// // Prints: "10"
|
||||
///
|
||||
/// After this publisher receives a request for more than 0 items, it requests
|
||||
/// unlimited items from its upstream publisher.
|
||||
///
|
||||
@@ -37,11 +65,36 @@ extension Publisher {
|
||||
/// Publishes the minimum value received from the upstream publisher, after it
|
||||
/// finishes.
|
||||
///
|
||||
/// Use `min(by:)` to determine the minimum value in the stream of elements from
|
||||
/// an upstream publisher using a comparison operation you specify.
|
||||
///
|
||||
/// This operator is useful when the value received from the upstream publisher isn’t
|
||||
/// `Comparable`.
|
||||
///
|
||||
/// In the example below an array publishes enumeration elements representing playing
|
||||
/// card ranks. The `min(by:)` operator compares the current and next elements using
|
||||
/// the `rawValue` property of each enumeration value in the user supplied closure and
|
||||
/// prints the minimum value found after publishing all of the elements.
|
||||
///
|
||||
/// enum Rank: Int {
|
||||
/// case ace = 1, two, three, four, five, six, seven, eight, nine,
|
||||
/// ten, jack, queen, king
|
||||
/// }
|
||||
///
|
||||
/// let cards: [Rank] = [.five, .queen, .ace, .eight, .king]
|
||||
/// cancellable = cards.publisher
|
||||
/// .min {
|
||||
/// return $0.rawValue < $1.rawValue
|
||||
/// }
|
||||
/// .sink { print("\($0)") }
|
||||
///
|
||||
/// // Prints: "ace"
|
||||
///
|
||||
/// After this publisher receives a request for more than 0 items, it requests
|
||||
/// unlimited items from its upstream publisher.
|
||||
///
|
||||
/// - Parameter areInIncreasingOrder: A closure that receives two elements and returns
|
||||
/// `true` if they are in increasing order.
|
||||
/// `true` if they’re in increasing order.
|
||||
/// - Returns: A publisher that publishes the minimum value received from the upstream
|
||||
/// publisher, after the upstream publisher finishes.
|
||||
public func min(
|
||||
@@ -50,15 +103,39 @@ extension Publisher {
|
||||
return max(by: { areInIncreasingOrder($1, $0) })
|
||||
}
|
||||
|
||||
/// Publishes the minimum value received from the upstream publisher, using the
|
||||
/// provided error-throwing closure to order the items.
|
||||
/// Publishes the minimum value received from the upstream publisher, using
|
||||
/// the provided error-throwing closure to order the items.
|
||||
///
|
||||
/// Use `tryMin(by:)` to determine the minimum value of elements received from
|
||||
/// the upstream publisher using an error-throwing closure you specify.
|
||||
///
|
||||
/// In the example below, an array publishes elements. The `tryMin(by:)` operator
|
||||
/// executes the error-throwing closure that throws when the `first` element is an odd
|
||||
/// number, terminating the publisher.
|
||||
///
|
||||
/// struct IllegalValueError: Error {}
|
||||
///
|
||||
/// let numbers: [Int] = [0, 10, 6, 13, 22, 22]
|
||||
/// numbers.publisher
|
||||
/// .tryMin { first, second -> Bool in
|
||||
/// if (first % 2 != 0) {
|
||||
/// throw IllegalValueError()
|
||||
/// }
|
||||
/// return first < second
|
||||
/// }
|
||||
/// .sink(
|
||||
/// receiveCompletion: { print ("completion: \($0)") },
|
||||
/// receiveValue: { print ("value: \($0)") }
|
||||
/// )
|
||||
///
|
||||
/// // Prints: "completion: failure(IllegalValueError())"
|
||||
///
|
||||
/// After this publisher receives a request for more than 0 items, it requests
|
||||
/// unlimited items from its upstream publisher.
|
||||
///
|
||||
/// - Parameter areInIncreasingOrder: A throwing closure that receives two elements
|
||||
/// and returns `true` if they are in increasing order. If this closure throws, the
|
||||
/// publisher terminates with a `Failure`.
|
||||
/// and returns `true` if they’re in increasing order. If this closure throws,
|
||||
/// the publisher terminates with a `Subscribers.Completion.failure(_:)`.
|
||||
/// - Returns: A publisher that publishes the minimum value received from the upstream
|
||||
/// publisher, after the upstream publisher finishes.
|
||||
public func tryMin(
|
||||
@@ -67,14 +144,36 @@ extension Publisher {
|
||||
return tryMax(by: { try areInIncreasingOrder($1, $0) })
|
||||
}
|
||||
|
||||
/// Publishes the maximum value received from the upstream publisher, using the
|
||||
/// provided ordering closure.
|
||||
/// Publishes the maximum value received from the upstream publisher, using
|
||||
/// the provided ordering closure.
|
||||
///
|
||||
/// Use `max(by:)` to determine the maximum value of elements received from
|
||||
/// the upstream publisher based on an ordering closure you specify.
|
||||
///
|
||||
/// In the example below, an array publishes enumeration elements representing playing
|
||||
/// card ranks. The `max(by:)` operator compares the current and next elements using
|
||||
/// the `rawValue` property of each enumeration value in the user supplied closure and
|
||||
/// prints the maximum value found after publishing all of the elements.
|
||||
///
|
||||
/// enum Rank: Int {
|
||||
/// case ace = 1, two, three, four, five, six, seven, eight, nine,
|
||||
/// ten, jack, queen, king
|
||||
/// }
|
||||
///
|
||||
/// let cards: [Rank] = [.five, .queen, .ace, .eight, .jack]
|
||||
/// cancellable = cards.publisher
|
||||
/// .max {
|
||||
/// return $0.rawValue > $1.rawValue
|
||||
/// }
|
||||
/// .sink { print("\($0)") }
|
||||
///
|
||||
/// // Prints: "queen"
|
||||
///
|
||||
/// After this publisher receives a request for more than 0 items, it requests
|
||||
/// unlimited items from its upstream publisher.
|
||||
///
|
||||
/// - Parameter areInIncreasingOrder: A closure that receives two elements and returns
|
||||
/// `true` if they are in increasing order.
|
||||
/// `true` if they’re in increasing order.
|
||||
/// - Returns: A publisher that publishes the maximum value received from the upstream
|
||||
/// publisher, after the upstream publisher finishes.
|
||||
public func max(
|
||||
@@ -83,16 +182,42 @@ extension Publisher {
|
||||
return .init(upstream: self, areInIncreasingOrder: areInIncreasingOrder)
|
||||
}
|
||||
|
||||
/// Publishes the maximum value received from the upstream publisher, using the
|
||||
/// provided error-throwing closure to order the items.
|
||||
/// Publishes the maximum value received from the upstream publisher, using
|
||||
/// the provided error-throwing closure to order the items.
|
||||
///
|
||||
/// Use `tryMax(by:)` to determine the maximum value of elements received from
|
||||
/// the upstream publisher using an error-throwing closure you specify.
|
||||
///
|
||||
/// In the example below, an array publishes elements. The `tryMax(by:)` operator
|
||||
/// executes the error-throwing closure that throws when the `first` element is
|
||||
/// an odd number, terminating the publisher.
|
||||
///
|
||||
/// struct IllegalValueError: Error {}
|
||||
///
|
||||
/// let numbers: [Int] = [0, 10, 6, 13, 22, 22]
|
||||
/// cancellable = numbers.publisher
|
||||
/// .tryMax { first, second -> Bool in
|
||||
/// if (first % 2 != 0) {
|
||||
/// throw IllegalValueError()
|
||||
/// }
|
||||
/// return first > second
|
||||
/// }
|
||||
/// .sink(
|
||||
/// receiveCompletion: { print ("completion: \($0)") },
|
||||
/// receiveValue: { print ("value: \($0)") }
|
||||
/// )
|
||||
///
|
||||
/// // Prints: completion: failure(IllegalValueError())
|
||||
///
|
||||
/// After this publisher receives a request for more than 0 items, it requests
|
||||
/// unlimited items from its upstream publisher.
|
||||
///
|
||||
/// - Parameter areInIncreasingOrder: A throwing closure that receives two elements
|
||||
/// and returns `true` if they are in increasing order. If this closure throws, the
|
||||
/// publisher terminates with a `Failure`.
|
||||
/// and returns `true` if they’re in increasing order. If this closure throws,
|
||||
/// the publisher terminates with a ``Subscribers/Completion/failure(_:)``.
|
||||
///
|
||||
/// - Returns: A publisher that publishes the maximum value received from the upstream
|
||||
/// publisher, after the upstream publisher finishes.
|
||||
/// publisher, after the upstream publisher finishes.
|
||||
public func tryMax(
|
||||
by areInIncreasingOrder: @escaping (Output, Output) throws -> Bool
|
||||
) -> Publishers.TryComparison<Self> {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user