Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b6aea2957a | |||
| 9219c8841c | |||
| 5a41e2976c | |||
| e48d5d4492 | |||
| 9b81672df8 | |||
| d5944290e6 | |||
| a3e361a29a | |||
| 0dc2015ec6 | |||
| 1e6a7cd3b6 | |||
| acb1b5996a | |||
| 77e7ff75fb | |||
| 26d6dc9971 | |||
| 280ea0ac9a | |||
| 8798739caa | |||
| c6c79144c5 |
+148
-22
@@ -1,17 +1,16 @@
|
||||
version: 2
|
||||
jobs:
|
||||
"Execute tests on macOS 10.15.0 (Xcode 11.3.0, Swift 5.1.3)":
|
||||
"Execute tests on macOS 10.15.0 (Xcode 11.2.1, Swift 5.1.2)":
|
||||
macos:
|
||||
xcode: "11.3.0"
|
||||
xcode: "11.2.1"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.3"
|
||||
SWIFT_VERSION: "5.1.2"
|
||||
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 \
|
||||
@@ -21,17 +20,15 @@ jobs:
|
||||
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"
|
||||
command: make generate-xcodeproj
|
||||
- run:
|
||||
name: Building for testing on macOS 10.15.0 with xcodebuild
|
||||
command: |
|
||||
@@ -63,35 +60,35 @@ jobs:
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash) -D DerivedData
|
||||
|
||||
"Execute compatibility tests on iOS 13.3 (Xcode 11.3.0, Swift 5.1.3)":
|
||||
"Execute compatibility tests on iOS 13.2.2 (Xcode 11.2.1, Swift 5.1.2)":
|
||||
macos:
|
||||
xcode: "11.3.0"
|
||||
xcode: "11.2.1"
|
||||
environment:
|
||||
SWIFT_VERSION: "5.1.3"
|
||||
SWIFT_VERSION: "5.1.2"
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: make generate-compatibility-xcodeproj
|
||||
- run:
|
||||
name: Building for testing on iOS 13.3 with xcodebuild
|
||||
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.3" \
|
||||
-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 against Combine on iOS 13.3 with xcodebuild
|
||||
name: Testing against Combine 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.3" \
|
||||
-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
|
||||
@@ -128,7 +125,7 @@ jobs:
|
||||
- run:
|
||||
name: Generating Xcode project
|
||||
command: |
|
||||
make generate-xcodeproj SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors"
|
||||
make generate-xcodeproj
|
||||
xcodebuild -scheme OpenCombine-Package -showdestinations
|
||||
- run:
|
||||
name: Building for testing on iOS 9.3 with xcodebuild
|
||||
@@ -161,6 +158,135 @@ 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)":
|
||||
docker:
|
||||
- image: swift:5.1.1-bionic
|
||||
@@ -185,7 +311,6 @@ jobs:
|
||||
> /dev/null 2>&1 \
|
||||
|| true
|
||||
make test-debug \
|
||||
SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--enable-code-coverage \
|
||||
@@ -198,7 +323,6 @@ jobs:
|
||||
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_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--build-path .build-test-debug-sanitize-thread" \
|
||||
@@ -212,7 +336,6 @@ jobs:
|
||||
name: Building and running tests in release mode
|
||||
command: |
|
||||
make test-release \
|
||||
SWIFT_BUILD_FLAGS="-Xswiftc -warnings-as-errors" \
|
||||
SWIFT_TEST_FLAGS="--enable-test-discovery \
|
||||
--enable-index-store \
|
||||
--build-path .build-test-release"
|
||||
@@ -223,7 +346,7 @@ jobs:
|
||||
|
||||
"Run SwiftLint and Danger":
|
||||
macos:
|
||||
xcode: "11.3.0"
|
||||
xcode: "11.2.0"
|
||||
environment:
|
||||
HOMEBREW_NO_AUTO_UPDATE: "1"
|
||||
steps:
|
||||
@@ -242,7 +365,7 @@ jobs:
|
||||
|
||||
"Run Pod spec lint":
|
||||
macos:
|
||||
xcode: "11.3.0"
|
||||
xcode: "11.2.0"
|
||||
environment:
|
||||
HOMEBREW_NO_AUTO_UPDATE: "1"
|
||||
steps:
|
||||
@@ -256,13 +379,16 @@ workflows:
|
||||
version: 2
|
||||
"OpenCombine: execute tests on macOS":
|
||||
jobs:
|
||||
- "Execute tests on macOS 10.15.0 (Xcode 11.3.0, Swift 5.1.3)"
|
||||
- "Execute tests on macOS 10.15.0 (Xcode 11.2.1, Swift 5.1.2)"
|
||||
"OpenCombine: execute compatibility tests":
|
||||
jobs:
|
||||
- "Execute compatibility tests on iOS 13.3 (Xcode 11.3.0, Swift 5.1.3)"
|
||||
- "Execute compatibility tests on iOS 13.2.2 (Xcode 11.2.1, Swift 5.1.2)"
|
||||
"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)"
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -DOPENCOMBINE_COMPATIBILITY_TEST
|
||||
#include "OpenCombineSPM.xcconfig"
|
||||
@@ -0,0 +1,3 @@
|
||||
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
|
||||
@@ -0,0 +1,28 @@
|
||||
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
-1
@@ -18,7 +18,7 @@ GEM
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.5)
|
||||
emoji_regex (1.0.1)
|
||||
excon (0.71.0)
|
||||
excon (0.68.0)
|
||||
faraday (0.17.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
|
||||
@@ -8,8 +8,10 @@ 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 $(SWIFT_BUILD_FLAGS) $(SWIFT_TEST_FLAGS)
|
||||
$(SWIFT_EXE) test -c debug --sanitize address $(SWIFT_BUILD_FLAGS) $(SWIFT_TEST_FLAGS)
|
||||
|
||||
test-debug-sanitize-thread:
|
||||
$(SWIFT_EXE) test -c debug --sanitize thread $(SWIFT_BUILD_FLAGS) $(SWIFT_TEST_FLAGS)
|
||||
@@ -24,11 +26,14 @@ test-compatibility:
|
||||
$(SWIFT_EXE) test -Xswiftc -DOPENCOMBINE_COMPATIBILITY_TEST
|
||||
|
||||
generate-compatibility-xcodeproj:
|
||||
$(SWIFT_EXE) package generate-xcodeproj --xcconfig-overrides Combine-Compatibility.xcconfig; \
|
||||
$(SWIFT_EXE) package generate-xcodeproj \
|
||||
--xcconfig-overrides .xcconfigs/Combine-Compatibility.xcconfig; \
|
||||
open OpenCombine.xcodeproj
|
||||
|
||||
generate-xcodeproj:
|
||||
$(SWIFT_EXE) package $(SWIFT_BUILD_FLAGS) generate-xcodeproj --enable-code-coverage
|
||||
$(SWIFT_EXE) package $(SWIFT_BUILD_FLAGS) generate-xcodeproj \
|
||||
--enable-code-coverage \
|
||||
--xcconfig-overrides .xcconfigs/OpenCombineSPM.xcconfig
|
||||
|
||||
gyb:
|
||||
$(shell ./utils/recursively_gyb.sh)
|
||||
|
||||
+3
-5
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "OpenCombine"
|
||||
spec.version = "0.7.0"
|
||||
spec.version = "0.5.0"
|
||||
spec.summary = "Open source implementation of Apple's Combine framework for processing values over time."
|
||||
|
||||
spec.description = <<-DESC
|
||||
@@ -20,8 +20,6 @@ Pod::Spec.new do |spec|
|
||||
spec.watchos.deployment_target = "2.0"
|
||||
spec.tvos.deployment_target = "9.0"
|
||||
|
||||
spec.source_files = "Sources/COpenCombineHelpers/**/*.{h,cpp}", "Sources/OpenCombine/**/*.swift"
|
||||
spec.public_header_files = "Sources/COpenCombineHelpers/include/*.h"
|
||||
|
||||
spec.libraries = "c++"
|
||||
spec.source_files = "Sources/OpenCombine/**/*.swift"
|
||||
spec.dependency "COpenCombineHelpers"
|
||||
end
|
||||
@@ -1,10 +1,10 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "OpenCombineDispatch"
|
||||
spec.version = "0.7.0"
|
||||
spec.summary = "OpenCombine + Dispatch interoperability"
|
||||
spec.version = "0.5.0"
|
||||
spec.summary = "OpenCombine Dispatching"
|
||||
|
||||
spec.description = <<-DESC
|
||||
Extends `DispatchQueue` with conformance to the `Scheduler` protocol
|
||||
Extends `DispatchQueue` with new methods and nested types.
|
||||
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", '>= 0.6'
|
||||
spec.dependency "OpenCombine"
|
||||
end
|
||||
@@ -1,25 +0,0 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "OpenCombineFoundation"
|
||||
spec.version = "0.7.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.6'
|
||||
end
|
||||
+3
-7
@@ -7,19 +7,15 @@ let package = Package(
|
||||
products: [
|
||||
.library(name: "OpenCombine", targets: ["OpenCombine"]),
|
||||
.library(name: "OpenCombineDispatch", targets: ["OpenCombineDispatch"]),
|
||||
.library(name: "OpenCombineFoundation", targets: ["OpenCombineFoundation"]),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "COpenCombineHelpers"),
|
||||
.target(name: "COpenCombineHelpers", cxxSettings: [.headerSearchPath(".")]),
|
||||
.target(name: "OpenCombine", dependencies: ["COpenCombineHelpers"]),
|
||||
.target(name: "OpenCombineDispatch", dependencies: ["OpenCombine"]),
|
||||
.target(name: "OpenCombineFoundation", dependencies: ["OpenCombine",
|
||||
"COpenCombineHelpers"]),
|
||||
.testTarget(name: "OpenCombineTests",
|
||||
dependencies: ["OpenCombine",
|
||||
"OpenCombineDispatch",
|
||||
"OpenCombineFoundation"],
|
||||
"OpenCombineDispatch"],
|
||||
swiftSettings: [.unsafeFlags(["-enable-testing"])])
|
||||
],
|
||||
cxxLanguageStandard: .cxx1z
|
||||
cxxLanguageStandard: .cxx14
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
[](https://codecov.io/gh/broadwaylamb/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.
|
||||
@@ -23,7 +22,7 @@ To add `OpenCombine` to your [SPM](https://swift.org/package-manager/) package,
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/broadwaylamb/OpenCombine.git", from: "0.7.0")
|
||||
.package(url: "https://github.com/broadwaylamb/OpenCombine.git", from: "0.5.0")
|
||||
],
|
||||
targets: [
|
||||
.target(name: "MyAwesomePackage", dependencies: ["OpenCombine", "OpenCombineDispatch"])
|
||||
@@ -44,8 +43,8 @@ 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.7'
|
||||
pod 'OpenCombineDispatch', '~> 0.7'
|
||||
pod 'OpenCombine', '~> 0.5.0'
|
||||
pod 'OpenCombineDispatch', '~> 0.5.0'
|
||||
```
|
||||
|
||||
### Contributing
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,163 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
|
||||
extension Timer {
|
||||
public static func publish(every interval: Foundation.TimeInterval, tolerance: Foundation.TimeInterval? = nil, on runLoop: Foundation.RunLoop, in mode: Foundation.RunLoop.Mode, options: Foundation.RunLoop.SchedulerOptions? = nil) -> Foundation.Timer.TimerPublisher
|
||||
final public class TimerPublisher : Combine.ConnectablePublisher {
|
||||
public typealias Output = Foundation.Date
|
||||
public typealias Failure = Swift.Never
|
||||
final public let interval: Foundation.TimeInterval
|
||||
final public let tolerance: Foundation.TimeInterval?
|
||||
final public let runLoop: Foundation.RunLoop
|
||||
final public let mode: Foundation.RunLoop.Mode
|
||||
final public let options: Foundation.RunLoop.SchedulerOptions?
|
||||
public init(interval: Foundation.TimeInterval, tolerance: Foundation.TimeInterval? = nil, runLoop: Foundation.RunLoop, mode: Foundation.RunLoop.Mode, options: Foundation.RunLoop.SchedulerOptions? = nil)
|
||||
final public func receive<S>(subscriber: S) where S : Combine.Subscriber, S.Failure == Foundation.Timer.TimerPublisher.Failure, S.Input == Foundation.Timer.TimerPublisher.Output
|
||||
final public func connect() -> Combine.Cancellable
|
||||
@objc deinit
|
||||
}
|
||||
}
|
||||
|
||||
extension OperationQueue : Combine.Scheduler {
|
||||
public struct SchedulerTimeType : Swift.Strideable, Swift.Codable, Swift.Hashable {
|
||||
public var date: Foundation.Date
|
||||
public init(_ date: Foundation.Date)
|
||||
public func distance(to other: Foundation.OperationQueue.SchedulerTimeType) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public func advanced(by n: Foundation.OperationQueue.SchedulerTimeType.Stride) -> Foundation.OperationQueue.SchedulerTimeType
|
||||
public struct Stride : Swift.ExpressibleByFloatLiteral, Swift.Comparable, Swift.SignedNumeric, Swift.Codable, Combine.SchedulerTimeIntervalConvertible {
|
||||
public typealias FloatLiteralType = Foundation.TimeInterval
|
||||
public typealias IntegerLiteralType = Foundation.TimeInterval
|
||||
public typealias Magnitude = Foundation.TimeInterval
|
||||
public var magnitude: Foundation.TimeInterval
|
||||
public var timeInterval: Foundation.TimeInterval {
|
||||
get
|
||||
}
|
||||
public init(integerLiteral value: Foundation.TimeInterval)
|
||||
public init(floatLiteral value: Foundation.TimeInterval)
|
||||
public init(_ timeInterval: Foundation.TimeInterval)
|
||||
public init?<T>(exactly source: T) where T : Swift.BinaryInteger
|
||||
public static func < (lhs: Foundation.OperationQueue.SchedulerTimeType.Stride, rhs: Foundation.OperationQueue.SchedulerTimeType.Stride) -> Swift.Bool
|
||||
public static func * (lhs: Foundation.OperationQueue.SchedulerTimeType.Stride, rhs: Foundation.OperationQueue.SchedulerTimeType.Stride) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public static func + (lhs: Foundation.OperationQueue.SchedulerTimeType.Stride, rhs: Foundation.OperationQueue.SchedulerTimeType.Stride) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public static func - (lhs: Foundation.OperationQueue.SchedulerTimeType.Stride, rhs: Foundation.OperationQueue.SchedulerTimeType.Stride) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public static func *= (lhs: inout Foundation.OperationQueue.SchedulerTimeType.Stride, rhs: Foundation.OperationQueue.SchedulerTimeType.Stride)
|
||||
public static func += (lhs: inout Foundation.OperationQueue.SchedulerTimeType.Stride, rhs: Foundation.OperationQueue.SchedulerTimeType.Stride)
|
||||
public static func -= (lhs: inout Foundation.OperationQueue.SchedulerTimeType.Stride, rhs: Foundation.OperationQueue.SchedulerTimeType.Stride)
|
||||
public static func seconds(_ s: Swift.Int) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public static func seconds(_ s: Swift.Double) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public static func milliseconds(_ ms: Swift.Int) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public static func microseconds(_ us: Swift.Int) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public static func nanoseconds(_ ns: Swift.Int) -> Foundation.OperationQueue.SchedulerTimeType.Stride
|
||||
public init(from decoder: Swift.Decoder) throws
|
||||
public func encode(to encoder: Swift.Encoder) throws
|
||||
public static func == (a: Foundation.OperationQueue.SchedulerTimeType.Stride, b: Foundation.OperationQueue.SchedulerTimeType.Stride) -> Swift.Bool
|
||||
}
|
||||
public init(from decoder: Swift.Decoder) throws
|
||||
public func encode(to encoder: Swift.Encoder) throws
|
||||
public var hashValue: Swift.Int {
|
||||
get
|
||||
}
|
||||
public func hash(into hasher: inout Swift.Hasher)
|
||||
}
|
||||
public struct SchedulerOptions {
|
||||
}
|
||||
public func schedule(options: Foundation.OperationQueue.SchedulerOptions?, _ action: @escaping () -> Swift.Void)
|
||||
public func schedule(after date: Foundation.OperationQueue.SchedulerTimeType, tolerance: Foundation.OperationQueue.SchedulerTimeType.Stride, options: Foundation.OperationQueue.SchedulerOptions?, _ action: @escaping () -> Swift.Void)
|
||||
public func schedule(after date: Foundation.OperationQueue.SchedulerTimeType, interval: Foundation.OperationQueue.SchedulerTimeType.Stride, tolerance: Foundation.OperationQueue.SchedulerTimeType.Stride, options: Foundation.OperationQueue.SchedulerOptions?, _ action: @escaping () -> Swift.Void) -> Combine.Cancellable
|
||||
public var now: Foundation.OperationQueue.SchedulerTimeType {
|
||||
get
|
||||
}
|
||||
public var minimumTolerance: Foundation.OperationQueue.SchedulerTimeType.Stride {
|
||||
get
|
||||
}
|
||||
}
|
||||
|
||||
extension RunLoop : Combine.Scheduler {
|
||||
public struct SchedulerTimeType : Swift.Strideable, Swift.Codable, Swift.Hashable {
|
||||
public var date: Foundation.Date
|
||||
public init(_ date: Foundation.Date)
|
||||
public func distance(to other: Foundation.RunLoop.SchedulerTimeType) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public func advanced(by n: Foundation.RunLoop.SchedulerTimeType.Stride) -> Foundation.RunLoop.SchedulerTimeType
|
||||
public struct Stride : Swift.ExpressibleByFloatLiteral, Swift.Comparable, Swift.SignedNumeric, Swift.Codable, Combine.SchedulerTimeIntervalConvertible {
|
||||
public typealias FloatLiteralType = Foundation.TimeInterval
|
||||
public typealias IntegerLiteralType = Foundation.TimeInterval
|
||||
public typealias Magnitude = Foundation.TimeInterval
|
||||
public var magnitude: Foundation.TimeInterval
|
||||
public var timeInterval: Foundation.TimeInterval {
|
||||
get
|
||||
}
|
||||
public init(integerLiteral value: Foundation.TimeInterval)
|
||||
public init(floatLiteral value: Foundation.TimeInterval)
|
||||
public init(_ timeInterval: Foundation.TimeInterval)
|
||||
public init?<T>(exactly source: T) where T : Swift.BinaryInteger
|
||||
public static func < (lhs: Foundation.RunLoop.SchedulerTimeType.Stride, rhs: Foundation.RunLoop.SchedulerTimeType.Stride) -> Swift.Bool
|
||||
public static func * (lhs: Foundation.RunLoop.SchedulerTimeType.Stride, rhs: Foundation.RunLoop.SchedulerTimeType.Stride) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public static func + (lhs: Foundation.RunLoop.SchedulerTimeType.Stride, rhs: Foundation.RunLoop.SchedulerTimeType.Stride) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public static func - (lhs: Foundation.RunLoop.SchedulerTimeType.Stride, rhs: Foundation.RunLoop.SchedulerTimeType.Stride) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public static func *= (lhs: inout Foundation.RunLoop.SchedulerTimeType.Stride, rhs: Foundation.RunLoop.SchedulerTimeType.Stride)
|
||||
public static func += (lhs: inout Foundation.RunLoop.SchedulerTimeType.Stride, rhs: Foundation.RunLoop.SchedulerTimeType.Stride)
|
||||
public static func -= (lhs: inout Foundation.RunLoop.SchedulerTimeType.Stride, rhs: Foundation.RunLoop.SchedulerTimeType.Stride)
|
||||
public static func seconds(_ s: Swift.Int) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public static func seconds(_ s: Swift.Double) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public static func milliseconds(_ ms: Swift.Int) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public static func microseconds(_ us: Swift.Int) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public static func nanoseconds(_ ns: Swift.Int) -> Foundation.RunLoop.SchedulerTimeType.Stride
|
||||
public init(from decoder: Swift.Decoder) throws
|
||||
public func encode(to encoder: Swift.Encoder) throws
|
||||
public static func == (a: Foundation.RunLoop.SchedulerTimeType.Stride, b: Foundation.RunLoop.SchedulerTimeType.Stride) -> Swift.Bool
|
||||
}
|
||||
public init(from decoder: Swift.Decoder) throws
|
||||
public func encode(to encoder: Swift.Encoder) throws
|
||||
public var hashValue: Swift.Int {
|
||||
get
|
||||
}
|
||||
public func hash(into hasher: inout Swift.Hasher)
|
||||
}
|
||||
public struct SchedulerOptions {
|
||||
}
|
||||
public func schedule(options: Foundation.RunLoop.SchedulerOptions?, _ action: @escaping () -> Swift.Void)
|
||||
public func schedule(after date: Foundation.RunLoop.SchedulerTimeType, tolerance: Foundation.RunLoop.SchedulerTimeType.Stride, options: Foundation.RunLoop.SchedulerOptions?, _ action: @escaping () -> Swift.Void)
|
||||
public func schedule(after date: Foundation.RunLoop.SchedulerTimeType, interval: Foundation.RunLoop.SchedulerTimeType.Stride, tolerance: Foundation.RunLoop.SchedulerTimeType.Stride, options: Foundation.RunLoop.SchedulerOptions?, _ action: @escaping () -> Swift.Void) -> Combine.Cancellable
|
||||
public var now: Foundation.RunLoop.SchedulerTimeType {
|
||||
get
|
||||
}
|
||||
public var minimumTolerance: Foundation.RunLoop.SchedulerTimeType.Stride {
|
||||
get
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// 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
|
||||
@@ -0,0 +1,42 @@
|
||||
//===--- 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) };
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// 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
-1
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// COpenCombineHelpers.cpp
|
||||
// Locking.cpp
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 23/09/2019.
|
||||
@@ -0,0 +1,123 @@
|
||||
//===--- 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,7 +9,8 @@
|
||||
#define COPENCOMBINEHELPERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if __has_attribute(swift_name)
|
||||
# define OPENCOMBINE_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
|
||||
@@ -17,12 +18,6 @@
|
||||
# define OPENCOMBINE_SWIFT_NAME(_name)
|
||||
#endif
|
||||
|
||||
#if __has_attribute(always_inline)
|
||||
# define OPENCOMBINE_ALWAYS_INLINE __attribute__((always_inline))
|
||||
#else
|
||||
# define OPENCOMBINE_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -30,59 +25,67 @@ 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 - Breakpoint
|
||||
#pragma mark - Type metadata
|
||||
|
||||
OPENCOMBINE_ALWAYS_INLINE
|
||||
inline void opencombine_stop_in_debugger(void) OPENCOMBINE_SWIFT_NAME(__stopInDebugger());
|
||||
typedef bool(*_Nonnull OpenCombineFieldEnumerator)(
|
||||
void* _Nullable enumeratorContext,
|
||||
const char* _Nonnull fieldName,
|
||||
size_t fieldOffset,
|
||||
const void* _Nonnull fieldTypeMetadataPtr
|
||||
);
|
||||
|
||||
void opencombine_stop_in_debugger(void) {
|
||||
raise(SIGTRAP);
|
||||
}
|
||||
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:));
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//===-- 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
|
||||
@@ -0,0 +1,435 @@
|
||||
//===- 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
|
||||
@@ -0,0 +1,61 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,409 @@
|
||||
//===-- 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_
|
||||
@@ -0,0 +1,414 @@
|
||||
//===- 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
|
||||
@@ -0,0 +1,590 @@
|
||||
//===-- 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
|
||||
@@ -0,0 +1,94 @@
|
||||
/*===-- 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 */
|
||||
@@ -0,0 +1,957 @@
|
||||
//===-- 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
|
||||
@@ -0,0 +1,166 @@
|
||||
//===- 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
|
||||
@@ -0,0 +1,198 @@
|
||||
//===- 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
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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 */
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// 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
@@ -0,0 +1,93 @@
|
||||
//===--- 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
@@ -0,0 +1,395 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,314 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,129 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,568 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,44 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,664 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,73 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,46 @@
|
||||
//===--- 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
|
||||
@@ -0,0 +1,37 @@
|
||||
//===--- 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
|
||||
@@ -5,16 +5,14 @@
|
||||
// Created by Sergej Jaskiewicz on 10.06.2019.
|
||||
//
|
||||
|
||||
#if canImport(COpenCombineHelpers)
|
||||
import COpenCombineHelpers
|
||||
#endif
|
||||
import func COpenCombineHelpers.nextCombineIdentifier
|
||||
|
||||
public struct CombineIdentifier: Hashable, CustomStringConvertible {
|
||||
|
||||
private let value: UInt64
|
||||
|
||||
public init() {
|
||||
value = __nextCombineIdentifier()
|
||||
value = nextCombineIdentifier()
|
||||
}
|
||||
|
||||
public init(_ obj: AnyObject) {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// 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.
|
||||
public final class CurrentValueSubject<Output, Failure: Error>: Subject {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// 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 {
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// 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,6 +5,8 @@
|
||||
// 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
|
||||
|
||||
@@ -5,12 +5,7 @@
|
||||
// Created by Sergej Jaskiewicz on 11.06.2019.
|
||||
//
|
||||
|
||||
#if canImport(COpenCombineHelpers)
|
||||
import COpenCombineHelpers
|
||||
#endif
|
||||
|
||||
internal typealias UnfairLock = __UnfairLock
|
||||
internal typealias UnfairRecursiveLock = __UnfairRecursiveLock
|
||||
|
||||
extension UnfairRecursiveLock {
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// 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
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// 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,
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
|
||||
/// A scheduler for performing synchronous actions.
|
||||
///
|
||||
/// 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.
|
||||
/// You can use this scheduler for immediate actions. If you attempt to schedule
|
||||
/// actions after a specific date, the scheduler produces a fatal error.
|
||||
public struct ImmediateScheduler: Scheduler {
|
||||
|
||||
/// The time type used by the immediate scheduler.
|
||||
|
||||
@@ -5,6 +5,61 @@
|
||||
// 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`
|
||||
@@ -41,23 +96,60 @@ public protocol ObservableObject: AnyObject {
|
||||
}
|
||||
|
||||
extension ObservableObject where ObjectWillChangePublisher == ObservableObjectPublisher {
|
||||
// swiftlint:disable let_var_whitespace
|
||||
#if swift(>=5.1)
|
||||
|
||||
/// A publisher that emits before the object has changed.
|
||||
@available(*, unavailable, message: """
|
||||
The default implementation of objectWillChange is not available yet. \
|
||||
It's being worked on in \
|
||||
https://github.com/broadwaylamb/OpenCombine/pull/97
|
||||
""")
|
||||
public var objectWillChange: ObservableObjectPublisher {
|
||||
fatalError("unimplemented")
|
||||
}
|
||||
#if swift(>=5.1)
|
||||
var installedPublisher: ObservableObjectPublisher?
|
||||
|
||||
enumerateFields(ofType: Self.self,
|
||||
allowResilientSuperclasses: false) { _, fieldOffset, fieldType in
|
||||
let storage = Unmanaged
|
||||
.passUnretained(self)
|
||||
.toOpaque()
|
||||
.advanced(by: fieldOffset)
|
||||
|
||||
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 {
|
||||
return publisher
|
||||
}
|
||||
let publisher = ObservableObjectPublisher()
|
||||
installedPublisher = publisher
|
||||
return publisher
|
||||
}
|
||||
|
||||
fieldType.installPublisher(lazilyCreatedPublisher, on: storage)
|
||||
|
||||
// Continue visiting other fields.
|
||||
return true
|
||||
}
|
||||
|
||||
return installedPublisher ?? ObservableObjectPublisher()
|
||||
#else
|
||||
public var objectWillChange: ObservableObjectPublisher {
|
||||
// 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)
|
||||
}
|
||||
#endif
|
||||
// swiftlint:enable let_var_whitespace
|
||||
}
|
||||
|
||||
/// The default publisher of an `ObservableObject`.
|
||||
@@ -67,126 +159,19 @@ public final class ObservableObjectPublisher: Publisher {
|
||||
|
||||
public typealias Failure = Never
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
private let subject: PassthroughSubject<Void, Never>
|
||||
|
||||
private var connections = Set<Conduit>()
|
||||
|
||||
// TODO: Combine needs this for some reason
|
||||
private var identifier: ObjectIdentifier?
|
||||
|
||||
public init() {}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
public init() {
|
||||
subject = .init()
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Input == Void, Downstream.Failure == Never
|
||||
{
|
||||
let inner = Inner(downstream: subscriber, parent: self)
|
||||
lock.lock()
|
||||
connections.insert(inner)
|
||||
lock.unlock()
|
||||
subscriber.receive(subscription: inner)
|
||||
subject.subscribe(subscriber)
|
||||
}
|
||||
|
||||
public func 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
|
||||
}
|
||||
subject.send()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 11.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
/// A subject that passes along values and completion.
|
||||
///
|
||||
/// Use a `PassthroughSubject` in unit tests when you want a publisher than can publish
|
||||
|
||||
@@ -1,952 +0,0 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ┃
|
||||
// ┃ Auto-generated from GYB template. DO NOT EDIT! ┃
|
||||
// ┃ ┃
|
||||
// ┃ ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
//
|
||||
// Publishers.Merge.swift.gyb
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 04/10/2019.
|
||||
//
|
||||
|
||||
// swiftlint:disable generic_type_name
|
||||
// swiftlint:disable vertical_parameter_alignment
|
||||
|
||||
// MARK: - Merge methods on Publisher
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Combines elements from this publisher with those from another publisher,
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - other: Another publisher.
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
public func merge<
|
||||
P: Publisher
|
||||
>(with other: P) -> Publishers.Merge<Self, P>
|
||||
where Failure == P.Failure, Output == P.Output
|
||||
{
|
||||
return .init(self, other)
|
||||
}
|
||||
/// Combines elements from this publisher with those from three other publishers,
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - b: A second publisher.
|
||||
/// - c: A third publisher.
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
public func merge<
|
||||
B: Publisher,
|
||||
C: Publisher
|
||||
>(with b: B,
|
||||
_ c: C) -> Publishers.Merge3<Self, B, C>
|
||||
where Failure == B.Failure, Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output
|
||||
{
|
||||
return .init(self, b, c)
|
||||
}
|
||||
/// Combines elements from this publisher with those from four other publishers,
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - b: A second publisher.
|
||||
/// - c: A third publisher.
|
||||
/// - d: A fourth publisher.
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
public func merge<
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher
|
||||
>(with b: B,
|
||||
_ c: C,
|
||||
_ d: D) -> Publishers.Merge4<Self, B, C, D>
|
||||
where Failure == B.Failure, Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output
|
||||
{
|
||||
return .init(self, b, c, d)
|
||||
}
|
||||
/// Combines elements from this publisher with those from five other publishers,
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - b: A second publisher.
|
||||
/// - c: A third publisher.
|
||||
/// - d: A fourth publisher.
|
||||
/// - e: A fifth publisher.
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
public func merge<
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher
|
||||
>(with b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E) -> Publishers.Merge5<Self, B, C, D, E>
|
||||
where Failure == B.Failure, Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output
|
||||
{
|
||||
return .init(self, b, c, d, e)
|
||||
}
|
||||
/// Combines elements from this publisher with those from six other publishers,
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - b: A second publisher.
|
||||
/// - c: A third publisher.
|
||||
/// - d: A fourth publisher.
|
||||
/// - e: A fifth publisher.
|
||||
/// - f: A sixth publisher.
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
public func merge<
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher,
|
||||
F: Publisher
|
||||
>(with b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E,
|
||||
_ f: F) -> Publishers.Merge6<Self, B, C, D, E, F>
|
||||
where Failure == B.Failure, Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output,
|
||||
E.Failure == F.Failure, E.Output == F.Output
|
||||
{
|
||||
return .init(self, b, c, d, e, f)
|
||||
}
|
||||
/// Combines elements from this publisher with those from seven other publishers,
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - b: A second publisher.
|
||||
/// - c: A third publisher.
|
||||
/// - d: A fourth publisher.
|
||||
/// - e: A fifth publisher.
|
||||
/// - f: A sixth publisher.
|
||||
/// - g: A seventh publisher.
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
public func merge<
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher,
|
||||
F: Publisher,
|
||||
G: Publisher
|
||||
>(with b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E,
|
||||
_ f: F,
|
||||
_ g: G) -> Publishers.Merge7<Self, B, C, D, E, F, G>
|
||||
where Failure == B.Failure, Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output,
|
||||
E.Failure == F.Failure, E.Output == F.Output,
|
||||
F.Failure == G.Failure, F.Output == G.Output
|
||||
{
|
||||
return .init(self, b, c, d, e, f, g)
|
||||
}
|
||||
/// Combines elements from this publisher with those from eight other publishers,
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - b: A second publisher.
|
||||
/// - c: A third publisher.
|
||||
/// - d: A fourth publisher.
|
||||
/// - e: A fifth publisher.
|
||||
/// - f: A sixth publisher.
|
||||
/// - g: A seventh publisher.
|
||||
/// - h: An eighth publisher.
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
public func merge<
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher,
|
||||
F: Publisher,
|
||||
G: Publisher,
|
||||
H: Publisher
|
||||
>(with b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E,
|
||||
_ f: F,
|
||||
_ g: G,
|
||||
_ h: H) -> Publishers.Merge8<Self, B, C, D, E, F, G, H>
|
||||
where Failure == B.Failure, Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output,
|
||||
E.Failure == F.Failure, E.Output == F.Output,
|
||||
F.Failure == G.Failure, F.Output == G.Output,
|
||||
G.Failure == H.Failure, G.Output == H.Output
|
||||
{
|
||||
return .init(self, b, c, d, e, f, g, h)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Combines elements from this publisher with those from another publisher of
|
||||
/// the same type, delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// - Parameter other: Another publisher of this publisher's type.
|
||||
/// - Returns: A publisher that emits an event when either upstream publisher emits
|
||||
/// an event.
|
||||
public func merge(with other: Self) -> Publishers.MergeMany<Self> {
|
||||
return .init([self, other])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Merge publishers
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher created by applying the merge function to two upstream
|
||||
/// publishers.
|
||||
public struct Merge<A: Publisher,
|
||||
B: Publisher>: Publisher
|
||||
where A.Failure == B.Failure, A.Output == B.Output
|
||||
{
|
||||
public typealias Output = A.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 A.Failure == Downstream.Failure,
|
||||
A.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: 2)
|
||||
a.subscribe(Merged.Side(index: 0, merger: merged))
|
||||
b.subscribe(Merged.Side(index: 1, merger: merged))
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
P: Publisher
|
||||
>(with other: P) -> Publishers.Merge3<A, B, P>
|
||||
{
|
||||
return .init(a, b, other)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y) -> Publishers.Merge4<A, B, Z, Y>
|
||||
{
|
||||
return .init(a, b, z, y)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X) -> Publishers.Merge5<A, B, Z, Y, X>
|
||||
{
|
||||
return .init(a, b, z, y, x)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher,
|
||||
W: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X,
|
||||
_ w: W) -> Publishers.Merge6<A, B, Z, Y, X, W>
|
||||
{
|
||||
return .init(a, b, z, y, x, w)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher,
|
||||
W: Publisher,
|
||||
V: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X,
|
||||
_ w: W,
|
||||
_ v: V) -> Publishers.Merge7<A, B, Z, Y, X, W, V>
|
||||
{
|
||||
return .init(a, b, z, y, x, w, v)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher,
|
||||
W: Publisher,
|
||||
V: Publisher,
|
||||
U: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X,
|
||||
_ w: W,
|
||||
_ v: V,
|
||||
_ u: U) -> Publishers.Merge8<A, B, Z, Y, X, W, V, U>
|
||||
{
|
||||
return .init(a, b, z, y, x, w, v, u)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher created by applying the merge function to three upstream
|
||||
/// publishers.
|
||||
public struct Merge3<A: Publisher,
|
||||
B: Publisher,
|
||||
C: Publisher>: Publisher
|
||||
where A.Failure == B.Failure, A.Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output
|
||||
{
|
||||
public typealias Output = A.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 A.Failure == Downstream.Failure,
|
||||
A.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: 3)
|
||||
a.subscribe(Merged.Side(index: 0, merger: merged))
|
||||
b.subscribe(Merged.Side(index: 1, merger: merged))
|
||||
c.subscribe(Merged.Side(index: 2, merger: merged))
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
P: Publisher
|
||||
>(with other: P) -> Publishers.Merge4<A, B, C, P>
|
||||
{
|
||||
return .init(a, b, c, other)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y) -> Publishers.Merge5<A, B, C, Z, Y>
|
||||
{
|
||||
return .init(a, b, c, z, y)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X) -> Publishers.Merge6<A, B, C, Z, Y, X>
|
||||
{
|
||||
return .init(a, b, c, z, y, x)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher,
|
||||
W: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X,
|
||||
_ w: W) -> Publishers.Merge7<A, B, C, Z, Y, X, W>
|
||||
{
|
||||
return .init(a, b, c, z, y, x, w)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher,
|
||||
W: Publisher,
|
||||
V: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X,
|
||||
_ w: W,
|
||||
_ v: V) -> Publishers.Merge8<A, B, C, Z, Y, X, W, V>
|
||||
{
|
||||
return .init(a, b, c, z, y, x, w, v)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher created by applying the merge function to four upstream
|
||||
/// publishers.
|
||||
public struct Merge4<A: Publisher,
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher>: Publisher
|
||||
where A.Failure == B.Failure, A.Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output
|
||||
{
|
||||
public typealias Output = A.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 A.Failure == Downstream.Failure,
|
||||
A.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: 4)
|
||||
a.subscribe(Merged.Side(index: 0, merger: merged))
|
||||
b.subscribe(Merged.Side(index: 1, merger: merged))
|
||||
c.subscribe(Merged.Side(index: 2, merger: merged))
|
||||
d.subscribe(Merged.Side(index: 3, merger: merged))
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
P: Publisher
|
||||
>(with other: P) -> Publishers.Merge5<A, B, C, D, P>
|
||||
{
|
||||
return .init(a, b, c, d, other)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y) -> Publishers.Merge6<A, B, C, D, Z, Y>
|
||||
{
|
||||
return .init(a, b, c, d, z, y)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X) -> Publishers.Merge7<A, B, C, D, Z, Y, X>
|
||||
{
|
||||
return .init(a, b, c, d, z, y, x)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher,
|
||||
W: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X,
|
||||
_ w: W) -> Publishers.Merge8<A, B, C, D, Z, Y, X, W>
|
||||
{
|
||||
return .init(a, b, c, d, z, y, x, w)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher created by applying the merge function to five upstream
|
||||
/// publishers.
|
||||
public struct Merge5<A: Publisher,
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher>: Publisher
|
||||
where A.Failure == B.Failure, A.Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output
|
||||
{
|
||||
public typealias Output = A.Output
|
||||
|
||||
public typealias Failure = A.Failure
|
||||
|
||||
public let a: A
|
||||
|
||||
public let b: B
|
||||
|
||||
public let c: C
|
||||
|
||||
public let d: D
|
||||
|
||||
public let e: E
|
||||
|
||||
public init(
|
||||
_ a: A,
|
||||
_ b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E
|
||||
) {
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
self.d = d
|
||||
self.e = e
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where A.Failure == Downstream.Failure,
|
||||
A.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: 5)
|
||||
a.subscribe(Merged.Side(index: 0, merger: merged))
|
||||
b.subscribe(Merged.Side(index: 1, merger: merged))
|
||||
c.subscribe(Merged.Side(index: 2, merger: merged))
|
||||
d.subscribe(Merged.Side(index: 3, merger: merged))
|
||||
e.subscribe(Merged.Side(index: 4, merger: merged))
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
P: Publisher
|
||||
>(with other: P) -> Publishers.Merge6<A, B, C, D, E, P>
|
||||
{
|
||||
return .init(a, b, c, d, e, other)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y) -> Publishers.Merge7<A, B, C, D, E, Z, Y>
|
||||
{
|
||||
return .init(a, b, c, d, e, z, y)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher,
|
||||
X: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y,
|
||||
_ x: X) -> Publishers.Merge8<A, B, C, D, E, Z, Y, X>
|
||||
{
|
||||
return .init(a, b, c, d, e, z, y, x)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher created by applying the merge function to six upstream
|
||||
/// publishers.
|
||||
public struct Merge6<A: Publisher,
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher,
|
||||
F: Publisher>: Publisher
|
||||
where A.Failure == B.Failure, A.Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output,
|
||||
E.Failure == F.Failure, E.Output == F.Output
|
||||
{
|
||||
public typealias Output = A.Output
|
||||
|
||||
public typealias Failure = A.Failure
|
||||
|
||||
public let a: A
|
||||
|
||||
public let b: B
|
||||
|
||||
public let c: C
|
||||
|
||||
public let d: D
|
||||
|
||||
public let e: E
|
||||
|
||||
public let f: F
|
||||
|
||||
public init(
|
||||
_ a: A,
|
||||
_ b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E,
|
||||
_ f: F
|
||||
) {
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
self.d = d
|
||||
self.e = e
|
||||
self.f = f
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where A.Failure == Downstream.Failure,
|
||||
A.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: 6)
|
||||
a.subscribe(Merged.Side(index: 0, merger: merged))
|
||||
b.subscribe(Merged.Side(index: 1, merger: merged))
|
||||
c.subscribe(Merged.Side(index: 2, merger: merged))
|
||||
d.subscribe(Merged.Side(index: 3, merger: merged))
|
||||
e.subscribe(Merged.Side(index: 4, merger: merged))
|
||||
f.subscribe(Merged.Side(index: 5, merger: merged))
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
P: Publisher
|
||||
>(with other: P) -> Publishers.Merge7<A, B, C, D, E, F, P>
|
||||
{
|
||||
return .init(a, b, c, d, e, f, other)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
Z: Publisher,
|
||||
Y: Publisher
|
||||
>(with z: Z,
|
||||
_ y: Y) -> Publishers.Merge8<A, B, C, D, E, F, Z, Y>
|
||||
{
|
||||
return .init(a, b, c, d, e, f, z, y)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher created by applying the merge function to seven upstream
|
||||
/// publishers.
|
||||
public struct Merge7<A: Publisher,
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher,
|
||||
F: Publisher,
|
||||
G: Publisher>: Publisher
|
||||
where A.Failure == B.Failure, A.Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output,
|
||||
E.Failure == F.Failure, E.Output == F.Output,
|
||||
F.Failure == G.Failure, F.Output == G.Output
|
||||
{
|
||||
public typealias Output = A.Output
|
||||
|
||||
public typealias Failure = A.Failure
|
||||
|
||||
public let a: A
|
||||
|
||||
public let b: B
|
||||
|
||||
public let c: C
|
||||
|
||||
public let d: D
|
||||
|
||||
public let e: E
|
||||
|
||||
public let f: F
|
||||
|
||||
public let g: G
|
||||
|
||||
public init(
|
||||
_ a: A,
|
||||
_ b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E,
|
||||
_ f: F,
|
||||
_ g: G
|
||||
) {
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
self.d = d
|
||||
self.e = e
|
||||
self.f = f
|
||||
self.g = g
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where A.Failure == Downstream.Failure,
|
||||
A.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: 7)
|
||||
a.subscribe(Merged.Side(index: 0, merger: merged))
|
||||
b.subscribe(Merged.Side(index: 1, merger: merged))
|
||||
c.subscribe(Merged.Side(index: 2, merger: merged))
|
||||
d.subscribe(Merged.Side(index: 3, merger: merged))
|
||||
e.subscribe(Merged.Side(index: 4, merger: merged))
|
||||
f.subscribe(Merged.Side(index: 5, merger: merged))
|
||||
g.subscribe(Merged.Side(index: 6, merger: merged))
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge<
|
||||
P: Publisher
|
||||
>(with other: P) -> Publishers.Merge8<A, B, C, D, E, F, G, P>
|
||||
{
|
||||
return .init(a, b, c, d, e, f, g, other)
|
||||
}
|
||||
}
|
||||
|
||||
/// A publisher created by applying the merge function to eight upstream
|
||||
/// publishers.
|
||||
public struct Merge8<A: Publisher,
|
||||
B: Publisher,
|
||||
C: Publisher,
|
||||
D: Publisher,
|
||||
E: Publisher,
|
||||
F: Publisher,
|
||||
G: Publisher,
|
||||
H: Publisher>: Publisher
|
||||
where A.Failure == B.Failure, A.Output == B.Output,
|
||||
B.Failure == C.Failure, B.Output == C.Output,
|
||||
C.Failure == D.Failure, C.Output == D.Output,
|
||||
D.Failure == E.Failure, D.Output == E.Output,
|
||||
E.Failure == F.Failure, E.Output == F.Output,
|
||||
F.Failure == G.Failure, F.Output == G.Output,
|
||||
G.Failure == H.Failure, G.Output == H.Output
|
||||
{
|
||||
public typealias Output = A.Output
|
||||
|
||||
public typealias Failure = A.Failure
|
||||
|
||||
public let a: A
|
||||
|
||||
public let b: B
|
||||
|
||||
public let c: C
|
||||
|
||||
public let d: D
|
||||
|
||||
public let e: E
|
||||
|
||||
public let f: F
|
||||
|
||||
public let g: G
|
||||
|
||||
public let h: H
|
||||
|
||||
public init(
|
||||
_ a: A,
|
||||
_ b: B,
|
||||
_ c: C,
|
||||
_ d: D,
|
||||
_ e: E,
|
||||
_ f: F,
|
||||
_ g: G,
|
||||
_ h: H
|
||||
) {
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
self.d = d
|
||||
self.e = e
|
||||
self.f = f
|
||||
self.g = g
|
||||
self.h = h
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where A.Failure == Downstream.Failure,
|
||||
A.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: 8)
|
||||
a.subscribe(Merged.Side(index: 0, merger: merged))
|
||||
b.subscribe(Merged.Side(index: 1, merger: merged))
|
||||
c.subscribe(Merged.Side(index: 2, merger: merged))
|
||||
d.subscribe(Merged.Side(index: 3, merger: merged))
|
||||
e.subscribe(Merged.Side(index: 4, merger: merged))
|
||||
f.subscribe(Merged.Side(index: 5, merger: merged))
|
||||
g.subscribe(Merged.Side(index: 6, merger: merged))
|
||||
h.subscribe(Merged.Side(index: 7, merger: merged))
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
public struct MergeMany<Upstream: Publisher>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
public let publishers: [Upstream]
|
||||
|
||||
public init(_ upstream: Upstream...) {
|
||||
self.publishers = upstream
|
||||
}
|
||||
|
||||
public init<UpstreamPublishers: Swift.Sequence>(_ upstream: UpstreamPublishers)
|
||||
where Upstream == UpstreamPublishers.Element
|
||||
{
|
||||
publishers = Array(upstream)
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: publishers.count)
|
||||
for (i, upstream) in publishers.enumerated() {
|
||||
upstream.subscribe(Merged.Side(index: i, merger: merged))
|
||||
}
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge(with other: Upstream) -> Publishers.MergeMany<Upstream> {
|
||||
var newPublishers = publishers
|
||||
newPublishers.append(other)
|
||||
return .init(newPublishers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable conformances
|
||||
|
||||
extension Publishers.Merge: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable {}
|
||||
|
||||
extension Publishers.Merge3: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable {}
|
||||
|
||||
extension Publishers.Merge4: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable,
|
||||
D: Equatable {}
|
||||
|
||||
extension Publishers.Merge5: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable,
|
||||
D: Equatable,
|
||||
E: Equatable {}
|
||||
|
||||
extension Publishers.Merge6: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable,
|
||||
D: Equatable,
|
||||
E: Equatable,
|
||||
F: Equatable {}
|
||||
|
||||
extension Publishers.Merge7: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable,
|
||||
D: Equatable,
|
||||
E: Equatable,
|
||||
F: Equatable,
|
||||
G: Equatable {}
|
||||
|
||||
extension Publishers.Merge8: Equatable
|
||||
where
|
||||
A: Equatable,
|
||||
B: Equatable,
|
||||
C: Equatable,
|
||||
D: Equatable,
|
||||
E: Equatable,
|
||||
F: Equatable,
|
||||
G: Equatable,
|
||||
H: Equatable {}
|
||||
|
||||
extension Publishers.MergeMany: Equatable
|
||||
where
|
||||
Upstream: Equatable {}
|
||||
@@ -249,26 +249,6 @@ 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 {
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
//
|
||||
// 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 this function for internal sanity checks that are active during testing but
|
||||
/// do not impact performance of shipping code.
|
||||
///
|
||||
/// - 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,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 18/09/2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension ConnectablePublisher {
|
||||
|
||||
/// Automates the process of connecting or disconnecting from this connectable
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
//
|
||||
// Publishers.Breakpoint.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 03.12.2019.
|
||||
//
|
||||
|
||||
#if canImport(COpenCombineHelpers)
|
||||
import COpenCombineHelpers
|
||||
#endif
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// - 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.
|
||||
///
|
||||
/// - Returns: A publisher that raises a debugger signal upon receiving a failure.
|
||||
public func breakpointOnError() -> Publishers.Breakpoint<Self> {
|
||||
return breakpoint { 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 {
|
||||
let children = CollectionOfOne<Mirror.Child>(
|
||||
("upstream", breakpoint.upstream)
|
||||
)
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
//
|
||||
// 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.
|
||||
///
|
||||
/// If the upstream publisher finishes before filling the buffer, this publisher sends
|
||||
/// an array of all the items it has 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.
|
||||
/// 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 }
|
||||
}
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
//
|
||||
// Publishers.Concatenate.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 24.10.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Prefixes a `Publisher`'s output with the specified sequence.
|
||||
///
|
||||
/// - Parameter elements: The elements to publish before this publisher’s elements.
|
||||
/// - Returns: A publisher that prefixes the specified elements prior to this
|
||||
/// publisher’s elements.
|
||||
public func prepend(
|
||||
_ elements: Output...
|
||||
) -> Publishers.Concatenate<Publishers.Sequence<[Output], Failure>, Self> {
|
||||
return prepend(elements)
|
||||
}
|
||||
|
||||
/// Prefixes a `Publisher`'s output with the specified sequence.
|
||||
///
|
||||
/// - Parameter elements: A sequence of elements to publish before this publisher’s
|
||||
/// elements.
|
||||
/// - Returns: A publisher that prefixes the sequence of elements prior to this
|
||||
/// publisher’s elements.
|
||||
public func prepend<Elements: Sequence>(
|
||||
_ elements: Elements
|
||||
) -> Publishers.Concatenate<Publishers.Sequence<Elements, Failure>, Self>
|
||||
where Output == Elements.Element
|
||||
{
|
||||
return prepend(.init(sequence: elements))
|
||||
}
|
||||
|
||||
/// Prefixes this publisher’s output with the elements emitted by the given publisher.
|
||||
///
|
||||
/// The resulting publisher doesn’t emit any elements until the prefixing publisher
|
||||
/// finishes.
|
||||
///
|
||||
/// - Parameter publisher: The prefixing publisher.
|
||||
/// - Returns: A publisher that prefixes the prefixing publisher’s elements prior to
|
||||
/// this publisher’s elements.
|
||||
public func prepend<Prefix: Publisher>(
|
||||
_ publisher: Prefix
|
||||
) -> Publishers.Concatenate<Prefix, Self>
|
||||
where Failure == Prefix.Failure, Output == Prefix.Output
|
||||
{
|
||||
return .init(prefix: publisher, suffix: self)
|
||||
}
|
||||
|
||||
/// Append a `Publisher`'s output with the specified sequence.
|
||||
public func append(
|
||||
_ elements: Output...
|
||||
) -> Publishers.Concatenate<Self, Publishers.Sequence<[Output], Failure>> {
|
||||
return append(elements)
|
||||
}
|
||||
|
||||
/// Appends a `Publisher`'s output with the specified sequence.
|
||||
public func append<Elements: Sequence>(
|
||||
_ elements: Elements
|
||||
) -> Publishers.Concatenate<Self, Publishers.Sequence<Elements, Failure>>
|
||||
where Output == Elements.Element
|
||||
{
|
||||
return append(.init(sequence: elements))
|
||||
}
|
||||
|
||||
/// Appends this publisher’s output with the elements emitted by the given publisher.
|
||||
///
|
||||
/// This operator produces no elements until this publisher finishes. It then produces
|
||||
/// this publisher’s elements, followed by the given publisher’s elements.
|
||||
/// If this publisher fails with an error, the prefixing publisher does not publish
|
||||
/// the provided publisher’s elements.
|
||||
///
|
||||
/// - Parameter publisher: The appending publisher.
|
||||
/// - Returns: A publisher that appends the appending publisher’s elements after this
|
||||
/// publisher’s elements.
|
||||
public func append<Suffix: Publisher>(
|
||||
_ publisher: Suffix
|
||||
) -> Publishers.Concatenate<Self, Suffix>
|
||||
where Suffix.Failure == Failure, Suffix.Output == Output
|
||||
{
|
||||
return .init(prefix: self, suffix: publisher)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that emits all of one publisher’s elements before those from anothe
|
||||
/// publisher.
|
||||
public struct Concatenate<Prefix: Publisher, Suffix: Publisher>: Publisher
|
||||
where Prefix.Failure == Suffix.Failure, Prefix.Output == Suffix.Output
|
||||
{
|
||||
public typealias Output = Suffix.Output
|
||||
|
||||
public typealias Failure = Suffix.Failure
|
||||
|
||||
/// The publisher to republish, in its entirety, before republishing elements from
|
||||
/// `suffix`.
|
||||
public let prefix: Prefix
|
||||
|
||||
/// The publisher to republish only after `prefix` finishes.
|
||||
public let suffix: Suffix
|
||||
|
||||
public init(prefix: Prefix, suffix: Suffix) {
|
||||
self.prefix = prefix
|
||||
self.suffix = suffix
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Suffix.Failure == Downstream.Failure, Suffix.Output == Downstream.Input
|
||||
{
|
||||
let inner = Inner(downstream: subscriber, suffix: suffix)
|
||||
prefix.subscribe(inner)
|
||||
subscriber.receive(subscription: inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.Concatenate: Equatable where Prefix: Equatable, Suffix: Equatable {}
|
||||
|
||||
extension Publishers.Concatenate {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Suffix.Output, Downstream.Failure == Suffix.Failure
|
||||
{
|
||||
typealias Input = Suffix.Output
|
||||
|
||||
typealias Failure = Suffix.Failure
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
private let suffix: Suffix
|
||||
|
||||
private var prefixFinished = false
|
||||
|
||||
private var demand = Subscribers.Demand.none
|
||||
|
||||
private var upstream: Subscription?
|
||||
|
||||
private var expectedSubscriptions = 2
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private let downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
fileprivate init(downstream: Downstream, suffix: Suffix) {
|
||||
self.downstream = downstream
|
||||
self.suffix = suffix
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard upstream == nil, expectedSubscriptions > 0 else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
upstream = subscription
|
||||
expectedSubscriptions -= 1
|
||||
let demand = self.demand
|
||||
lock.unlock()
|
||||
if demand > 0 {
|
||||
subscription.request(demand)
|
||||
}
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
demand -= 1
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(input)
|
||||
downstreamLock.unlock()
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
return newDemand
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
// Reading prefixFinished should be locked. Combine doesn't lock here.
|
||||
if prefixFinished {
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
guard case .finished = completion else {
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
prefixFinished = true // Should be locked as well?
|
||||
lock.lock()
|
||||
upstream = nil
|
||||
lock.unlock()
|
||||
suffix.subscribe(self)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
self.demand += demand
|
||||
guard let subscription = upstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard let subscription = upstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
upstream = nil
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "Concatenate" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
let children: [Mirror.Child] = [
|
||||
("downstream", downstream),
|
||||
("upstreamSubscription", upstream as Any),
|
||||
("suffix", suffix),
|
||||
("demand", demand)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
//
|
||||
// Publishers.Delay.swift
|
||||
// OpenCombine
|
||||
//
|
||||
// Created by Евгений Богомолов on 07/09/2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Delays delivery of all output to the downstream receiver by a specified amount
|
||||
/// of time on a particular scheduler.
|
||||
///
|
||||
/// The delay affects the delivery of elements and completion, but not of the original
|
||||
/// subscription.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - interval: The amount of time to delay.
|
||||
/// - tolerance: The allowed tolerance in firing delayed events.
|
||||
/// - scheduler: The scheduler to deliver the delayed events.
|
||||
/// - Returns: A publisher that delays delivery of elements and completion to
|
||||
/// the downstream receiver.
|
||||
public func delay<Context: Scheduler>(
|
||||
for interval: Context.SchedulerTimeType.Stride,
|
||||
tolerance: Context.SchedulerTimeType.Stride? = nil,
|
||||
scheduler: Context,
|
||||
options: Context.SchedulerOptions? = nil
|
||||
) -> Publishers.Delay<Self, Context> {
|
||||
return .init(upstream: self,
|
||||
interval: interval,
|
||||
tolerance: tolerance ?? scheduler.minimumTolerance,
|
||||
scheduler: scheduler,
|
||||
options: options)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that delays delivery of elements and completion
|
||||
/// to the downstream receiver.
|
||||
public struct Delay<Upstream: Publisher, Context: Scheduler>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher that this publisher receives elements from.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// The amount of time to delay.
|
||||
public let interval: Context.SchedulerTimeType.Stride
|
||||
|
||||
/// The allowed tolerance in firing delayed events.
|
||||
public let tolerance: Context.SchedulerTimeType.Stride
|
||||
|
||||
/// The scheduler to deliver the delayed events.
|
||||
public let scheduler: Context
|
||||
|
||||
public let options: Context.SchedulerOptions?
|
||||
|
||||
public init(upstream: Upstream,
|
||||
interval: Context.SchedulerTimeType.Stride,
|
||||
tolerance: Context.SchedulerTimeType.Stride,
|
||||
scheduler: Context,
|
||||
options: Context.SchedulerOptions? = nil)
|
||||
{
|
||||
self.upstream = upstream
|
||||
self.interval = interval
|
||||
self.tolerance = tolerance
|
||||
self.scheduler = scheduler
|
||||
self.options = options
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
upstream.subscribe(Inner(self, downstream: subscriber))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.Delay {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
Subscription
|
||||
where Downstream.Input == Upstream.Output, Downstream.Failure == Upstream.Failure
|
||||
{
|
||||
// NOTE: This class has been audited for thread safety
|
||||
|
||||
typealias Input = Upstream.Output
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
fileprivate typealias Delay = Publishers.Delay<Upstream, Context>
|
||||
|
||||
private enum State {
|
||||
case ready(Delay, Downstream)
|
||||
case subscribed(Delay, Downstream, Subscription)
|
||||
case terminal
|
||||
}
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
private var state: State
|
||||
private let downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
fileprivate init(_ publisher: Delay, downstream: Downstream) {
|
||||
state = .ready(publisher, downstream)
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
private func schedule(_ delay: Delay, work: @escaping () -> Void) {
|
||||
delay
|
||||
.scheduler
|
||||
.schedule(after: delay.scheduler.now.advanced(by: delay.interval),
|
||||
tolerance: delay.tolerance,
|
||||
options: delay.options,
|
||||
work)
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case let .ready(delay, downstream) = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .subscribed(delay, downstream, subscription)
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
downstream.receive(subscription: self)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
func receive(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
guard case let .subscribed(delay, downstream, _) = state else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
lock.unlock()
|
||||
schedule(delay) {
|
||||
self.scheduledReceive(input, downstream: downstream)
|
||||
}
|
||||
return .none
|
||||
}
|
||||
|
||||
private func scheduledReceive(_ input: Upstream.Output, downstream: Downstream) {
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(input)
|
||||
downstreamLock.unlock()
|
||||
guard newDemand > 0 else {
|
||||
return
|
||||
}
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscription.request(newDemand)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(delay, downstream, _) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
schedule(delay) {
|
||||
self.scheduledReceive(completion: completion, downstream: downstream)
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduledReceive(completion: Subscribers.Completion<Failure>,
|
||||
downstream: Downstream) {
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sven Weidauer on 03.10.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
/// Omits the specified number of elements before republishing subsequent elements.
|
||||
///
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
//
|
||||
// Publishers.DropUntilOutput.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 24.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Ignores elements from the upstream publisher until it receives an element from
|
||||
/// a second publisher.
|
||||
///
|
||||
/// This publisher requests a single value from the upstream publisher, and it ignores
|
||||
/// (drops) all elements from that publisher until the upstream publisher produces
|
||||
/// a value. After the `other` publisher produces an element, this publisher cancels
|
||||
/// its subscription to the `other` publisher, and allows events from the `upstream`
|
||||
/// publisher to pass through.
|
||||
/// After this publisher receives a subscription from the upstream publisher, it
|
||||
/// passes through backpressure requests from downstream to the upstream publisher.
|
||||
/// If the upstream publisher acts on those requests before the other publisher
|
||||
/// produces an item, this publisher drops the elements it receives from the upstream
|
||||
/// publisher.
|
||||
///
|
||||
/// - Parameter publisher: A publisher to monitor for its first emitted element.
|
||||
/// - Returns: A publisher that drops elements from the upstream publisher until the
|
||||
/// `other` publisher produces a value.
|
||||
public func drop<Other: Publisher>(
|
||||
untilOutputFrom publisher: Other
|
||||
) -> Publishers.DropUntilOutput<Self, Other> where Failure == Other.Failure {
|
||||
return .init(upstream: self, other: publisher)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that ignores elements from the upstream publisher until it receives
|
||||
/// an element from second publisher.
|
||||
public struct DropUntilOutput<Upstream: Publisher, Other: Publisher>: Publisher
|
||||
where Upstream.Failure == Other.Failure
|
||||
{
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher that this publisher receives elements from.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// A publisher to monitor for its first emitted element.
|
||||
public let other: Other
|
||||
|
||||
/// Creates a publisher that ignores elements from the upstream publisher until
|
||||
/// it receives an element from another publisher.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - upstream: A publisher to drop elements from while waiting for another
|
||||
/// publisher to emit elements.
|
||||
/// - other: A publisher to monitor for its first emitted element.
|
||||
public init(upstream: Upstream, other: Other) {
|
||||
self.upstream = upstream
|
||||
self.other = other
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Output == Downstream.Input,
|
||||
Other.Failure == Downstream.Failure
|
||||
{
|
||||
let inner = Inner(downstream: subscriber)
|
||||
other.subscribe(Inner.OtherSubscriber(inner: inner))
|
||||
upstream.subscribe(inner)
|
||||
subscriber.receive(subscription: inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.DropUntilOutput: Equatable
|
||||
where Upstream: Equatable, Other: Equatable {}
|
||||
|
||||
extension Publishers.DropUntilOutput {
|
||||
fileprivate 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 var triggered = false
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private let downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
private var upstreamSubscription: Subscription?
|
||||
|
||||
private var pendingDemand = Subscribers.Demand.none
|
||||
|
||||
private var otherSubscription: Subscription?
|
||||
|
||||
private var otherFinished = false
|
||||
|
||||
private var cancelled = false
|
||||
|
||||
init(downstream: Downstream) {
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard upstreamSubscription == nil && !cancelled else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
upstreamSubscription = subscription
|
||||
if pendingDemand > 0 {
|
||||
lock.unlock()
|
||||
subscription.request(pendingDemand)
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func receive(_ input: Input) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
if !triggered || cancelled {
|
||||
pendingDemand -= 1
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(input)
|
||||
downstreamLock.unlock()
|
||||
return newDemand
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
lock.lock()
|
||||
if cancelled {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
cancelled = true
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
private func receiveOther(subscription: Subscription) {
|
||||
// Combine doesn't lock here
|
||||
guard otherSubscription == nil else {
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
otherSubscription = subscription
|
||||
subscription.request(.max(1))
|
||||
}
|
||||
|
||||
private func receiveOther(_ input: Other.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
triggered = true
|
||||
otherSubscription = nil
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
|
||||
private func receiveOther(completion: Subscribers.Completion<Other.Failure>) {
|
||||
lock.lock()
|
||||
if triggered {
|
||||
otherSubscription = nil
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
otherFinished = true
|
||||
if let upstreamSubscription = self.upstreamSubscription {
|
||||
self.upstreamSubscription = nil
|
||||
lock.unlock()
|
||||
upstreamSubscription.cancel()
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
pendingDemand += demand
|
||||
if let subscription = upstreamSubscription {
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
} else {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
let upstreamSubscription = self.upstreamSubscription
|
||||
let otherSubscription = self.otherSubscription
|
||||
self.upstreamSubscription = nil
|
||||
self.otherSubscription = nil
|
||||
cancelled = true
|
||||
lock.unlock()
|
||||
|
||||
upstreamSubscription?.cancel()
|
||||
otherSubscription?.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "DropUntilOutput" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
return Mirror(self, children: EmptyCollection())
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.DropUntilOutput.Inner {
|
||||
fileprivate struct OtherSubscriber
|
||||
: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
let inner: Publishers.DropUntilOutput<Upstream, Other>.Inner<Downstream>
|
||||
|
||||
var combineIdentifier: CombineIdentifier {
|
||||
return inner.combineIdentifier
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
inner.receiveOther(subscription: subscription)
|
||||
}
|
||||
|
||||
func receive(_ input: Other.Output) -> Subscribers.Demand {
|
||||
return inner.receiveOther(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Other.Failure>) {
|
||||
inner.receiveOther(completion: completion)
|
||||
}
|
||||
|
||||
var description: String { return "DropUntilOutput" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
return Mirror(self, children: EmptyCollection())
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 16.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Omits elements from the upstream publisher until a given closure returns false,
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// Created by Eric Patey on 16.08.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
/// Transforms all elements from an upstream publisher into a new or existing
|
||||
/// publisher.
|
||||
@@ -88,9 +90,10 @@ extension Publishers.FlatMap {
|
||||
/// by the `downstreamLock`.
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
// Must be recursive lock. Probably a bug in Combine.
|
||||
/// All the calls to the downstream subscriber should be made with this lock
|
||||
/// acquired.
|
||||
private let downstreamLock = UnfairRecursiveLock.allocate()
|
||||
private let downstreamLock = UnfairLock.allocate()
|
||||
|
||||
private let downstream: Downstream
|
||||
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
//
|
||||
// Publishers.HandleEvents.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 03.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Performs the specified closures when publisher events occur.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - receiveSubscription: A closure that executes when the publisher receives
|
||||
/// the subscription from the upstream publisher. Defaults to `nil`.
|
||||
/// - receiveOutput: A closure that executes when the publisher receives a value
|
||||
/// from the upstream publisher. Defaults to `nil`.
|
||||
/// - receiveCompletion: A closure that executes when the publisher receives
|
||||
/// the completion from the upstream publisher. Defaults to `nil`.
|
||||
/// - receiveCancel: A closure that executes when the downstream receiver cancels
|
||||
/// publishing. Defaults to `nil`.
|
||||
/// - receiveRequest: A closure that executes when the publisher receives a request
|
||||
/// for more elements. Defaults to `nil`.
|
||||
/// - Returns: A publisher that performs the specified closures when publisher events
|
||||
/// occur.
|
||||
public func handleEvents(
|
||||
receiveSubscription: ((Subscription) -> Void)? = nil,
|
||||
receiveOutput: ((Output) -> Void)? = nil,
|
||||
receiveCompletion: ((Subscribers.Completion<Failure>) -> Void)? = nil,
|
||||
receiveCancel: (() -> Void)? = nil,
|
||||
receiveRequest: ((Subscribers.Demand) -> Void)? = nil
|
||||
) -> Publishers.HandleEvents<Self> {
|
||||
return .init(upstream: self,
|
||||
receiveSubscription: receiveSubscription,
|
||||
receiveOutput: receiveOutput,
|
||||
receiveCompletion: receiveCompletion,
|
||||
receiveCancel: receiveCancel,
|
||||
receiveRequest: receiveRequest)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that performs the specified closures when publisher events occur.
|
||||
public struct HandleEvents<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 the subscription from
|
||||
/// the upstream publisher.
|
||||
public var receiveSubscription: ((Subscription) -> Void)?
|
||||
|
||||
/// A closure that executes when the publisher receives a value from the upstream
|
||||
/// publisher.
|
||||
public var receiveOutput: ((Upstream.Output) -> Void)?
|
||||
|
||||
/// A closure that executes when the publisher receives the completion from
|
||||
/// the upstream publisher.
|
||||
public var receiveCompletion:
|
||||
((Subscribers.Completion<Upstream.Failure>) -> Void)?
|
||||
|
||||
/// A closure that executes when the downstream receiver cancels publishing.
|
||||
public var receiveCancel: (() -> Void)?
|
||||
|
||||
/// A closure that executes when the publisher receives a request for more
|
||||
/// elements.
|
||||
public var receiveRequest: ((Subscribers.Demand) -> Void)?
|
||||
|
||||
public init(
|
||||
upstream: Upstream,
|
||||
receiveSubscription: ((Subscription) -> Void)? = nil,
|
||||
receiveOutput: ((Output) -> Void)? = nil,
|
||||
receiveCompletion: ((Subscribers.Completion<Failure>) -> Void)? = nil,
|
||||
receiveCancel: (() -> Void)? = nil,
|
||||
receiveRequest: ((Subscribers.Demand) -> Void)?
|
||||
) {
|
||||
self.upstream = upstream
|
||||
self.receiveSubscription = receiveSubscription
|
||||
self.receiveOutput = receiveOutput
|
||||
self.receiveCompletion = receiveCompletion
|
||||
self.receiveCancel = receiveCancel
|
||||
self.receiveRequest = receiveRequest
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
let inner = Inner(self, downstream: subscriber)
|
||||
subscriber.receive(subscription: inner)
|
||||
upstream.subscribe(inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.HandleEvents {
|
||||
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 var status = SubscriptionStatus.awaitingSubscription
|
||||
private var pendingDemand = Subscribers.Demand.none
|
||||
private let lock = UnfairLock.allocate()
|
||||
private var events: Publishers.HandleEvents<Upstream>?
|
||||
private let downstream: Downstream
|
||||
|
||||
init(_ events: Publishers.HandleEvents<Upstream>, downstream: Downstream) {
|
||||
self.events = events
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
events?.receiveSubscription?(subscription)
|
||||
lock.lock()
|
||||
guard case .awaitingSubscription = status else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
status = .subscribed(subscription)
|
||||
let pendingDemand = self.pendingDemand
|
||||
self.pendingDemand = .none
|
||||
lock.unlock()
|
||||
if pendingDemand > 0 {
|
||||
subscription.request(pendingDemand)
|
||||
}
|
||||
}
|
||||
|
||||
func receive(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
events?.receiveOutput?(input)
|
||||
let newDemand = downstream.receive(input)
|
||||
if newDemand > 0 {
|
||||
events?.receiveRequest?(newDemand)
|
||||
}
|
||||
return newDemand
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
events?.receiveCompletion?(completion)
|
||||
lock.lock()
|
||||
events = nil
|
||||
status = .terminal
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
events?.receiveRequest?(demand)
|
||||
lock.lock()
|
||||
if case let .subscribed(subscription) = status {
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
return
|
||||
}
|
||||
pendingDemand += demand
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
events?.receiveCancel?()
|
||||
lock.lock()
|
||||
guard case let .subscribed(subscription) = status else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
events = nil
|
||||
status = .terminal
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "HandleEvents" }
|
||||
|
||||
var customMirror: Mirror { return Mirror(self, children: EmptyCollection()) }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
// Created by Eric Patey on 16.08.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Ingores all upstream elements, but passes along a completion
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Anton Nazarov on 25.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Transforms all elements from the upstream publisher with a provided closure.
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
//
|
||||
// Publishers.MeasureInterval.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 03.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Measures and emits the time interval between events received from an upstream
|
||||
/// publisher.
|
||||
///
|
||||
/// The output type of the returned scheduler is the time interval of the provided
|
||||
/// scheduler.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - scheduler: The scheduler on which to deliver elements.
|
||||
/// - options: Options that customize the delivery of elements.
|
||||
/// - Returns: A publisher that emits elements representing the time interval between
|
||||
/// the elements it receives.
|
||||
public func measureInterval<Context: Scheduler>(
|
||||
using scheduler: Context,
|
||||
options: Context.SchedulerOptions? = nil
|
||||
) -> Publishers.MeasureInterval<Self, Context> {
|
||||
return .init(upstream: self, scheduler: scheduler)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that measures and emits the time interval between events received from
|
||||
/// an upstream publisher.
|
||||
public struct MeasureInterval<Upstream: Publisher, Context: Scheduler>: Publisher {
|
||||
|
||||
public typealias Output = Context.SchedulerTimeType.Stride
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// The scheduler on which to deliver elements.
|
||||
public let scheduler: Context
|
||||
|
||||
public init(upstream: Upstream, scheduler: Context) {
|
||||
self.upstream = upstream
|
||||
self.scheduler = scheduler
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Downstream.Input == Context.SchedulerTimeType.Stride
|
||||
{
|
||||
upstream.subscribe(Inner(self, downstream: subscriber))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.MeasureInterval {
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Context.SchedulerTimeType.Stride,
|
||||
Downstream.Failure == Upstream.Failure
|
||||
{
|
||||
// NOTE: This class has been audited for thread safety
|
||||
|
||||
typealias Input = Upstream.Output
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
typealias MeasureInterval = Publishers.MeasureInterval<Upstream, Context>
|
||||
|
||||
private enum State {
|
||||
case ready(MeasureInterval, Downstream)
|
||||
case subscribed(MeasureInterval, Downstream, Subscription)
|
||||
case terminal
|
||||
}
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var state: State
|
||||
|
||||
private var last: Context.SchedulerTimeType?
|
||||
|
||||
init(_ measureInterval: MeasureInterval, downstream: Downstream) {
|
||||
state = .ready(measureInterval, downstream)
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case let .ready(measureInterval, downstream) = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .subscribed(measureInterval, downstream, subscription)
|
||||
last = measureInterval.scheduler.now
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receive(_: Input) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
guard case let .subscribed(measureInterval, downstream, subscription) = state,
|
||||
let previousTime = last else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
let now = measureInterval.scheduler.now
|
||||
last = now
|
||||
lock.unlock()
|
||||
let newDemand = downstream.receive(previousTime.distance(to: now))
|
||||
if newDemand > 0 {
|
||||
subscription.request(newDemand)
|
||||
}
|
||||
return .none
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Failure>) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, downstream, _) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
last = nil
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
last = nil
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "MeasureInterval" }
|
||||
|
||||
var customMirror: Mirror { return Mirror(self, children: EmptyCollection()) }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -1,285 +0,0 @@
|
||||
${template_header}
|
||||
//
|
||||
// Publishers.Merge.swift.gyb
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 04/10/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'),
|
||||
(5, 'five', 'A fifth'),
|
||||
(6, 'six', 'A sixth'),
|
||||
(7, 'seven', 'A seventh'),
|
||||
(8, 'eight', 'An eighth')]
|
||||
|
||||
def make_publisher_name(arity):
|
||||
return suffix_variadic('Merge', arity, arity - 1)
|
||||
|
||||
def make_upstream_types(arity, start=0):
|
||||
return [str(c) for c in string.ascii_uppercase[start:arity]]
|
||||
|
||||
def make_upstream_types_reversed(arity):
|
||||
return [str(c) for c in reversed(string.ascii_uppercase)][:arity]
|
||||
|
||||
def make_upstream_generic_constraints(upstream_types, first_is_self=False):
|
||||
|
||||
format_string = '{0}Failure == {1}.Failure, {0}Output == {1}.Output'
|
||||
|
||||
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_merge_method(arg_count, arity, indent_spaces_count):
|
||||
assert(arg_count <= arity - 1)
|
||||
is_specialization = arg_count < arity - 1
|
||||
|
||||
declaration_format = """\
|
||||
public func merge<
|
||||
{}
|
||||
>(with {}) -> Publishers.{}<{}>\
|
||||
"""
|
||||
|
||||
where_clause_format = '\n where {}'
|
||||
|
||||
if arg_count == 1:
|
||||
upstream_types = ['P']
|
||||
elif is_specialization:
|
||||
upstream_types = make_upstream_types_reversed(arg_count)
|
||||
else:
|
||||
upstream_types = make_upstream_types(arg_count + 1, 1)
|
||||
|
||||
method_generic_params = \
|
||||
[upstream_type + ': Publisher' for upstream_type in upstream_types]
|
||||
|
||||
cs_method_generic_params = \
|
||||
(',\n ').join(method_generic_params)
|
||||
|
||||
method_args = ['other: P'] \
|
||||
if arg_count == 1 else ['{}: {}'.format(upstream_type.lower(), upstream_type) \
|
||||
for upstream_type in upstream_types]
|
||||
|
||||
cs_method_args = ',\n _ '.join(method_args)
|
||||
|
||||
publisher_name = make_publisher_name(arity)
|
||||
|
||||
self_generic_params = make_upstream_types(arity - arg_count) \
|
||||
if is_specialization else ['Self']
|
||||
|
||||
publisher_generic_params = self_generic_params + upstream_types
|
||||
|
||||
cs_publisher_generic_params = ', '.join(publisher_generic_params)
|
||||
|
||||
generic_constraints = make_upstream_generic_constraints(upstream_types, True)
|
||||
|
||||
cs_generic_constraints = \
|
||||
',\n '.join(generic_constraints)
|
||||
|
||||
declaration = declaration_format.format(cs_method_generic_params,
|
||||
cs_method_args,
|
||||
publisher_name,
|
||||
cs_publisher_generic_params)
|
||||
|
||||
if not is_specialization:
|
||||
declaration += where_clause_format.format(cs_generic_constraints)
|
||||
|
||||
return indent(declaration, indent_spaces_count)
|
||||
}%
|
||||
|
||||
// swiftlint:disable generic_type_name
|
||||
// swiftlint:disable vertical_parameter_alignment
|
||||
|
||||
// MARK: - Merge methods on Publisher
|
||||
|
||||
extension Publisher {
|
||||
|
||||
% for arity, _, _ in instantiations:
|
||||
%
|
||||
% doc_cardinal = 'another publisher' \
|
||||
% if arity == 2 else (instantiations[arity - 2][1] + ' other publishers')
|
||||
% argument_names = ['other'] \
|
||||
% if arity == 2 else [upstream_type.lower() \
|
||||
% for upstream_type in make_upstream_types(arity, 1)]
|
||||
/// Combines elements from this publisher with those from ${doc_cardinal},
|
||||
/// delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// The merged publisher continues to emit elements until all upstream publishers
|
||||
/// finish. If an upstream publisher produces an error, the merged publisher fails
|
||||
/// with that error.
|
||||
///
|
||||
/// - Parameters:
|
||||
% for i in range(arity - 1):
|
||||
% param_doc = 'Another' if arity == 2 else instantiations[i][2]
|
||||
/// - ${argument_names[i]}: ${param_doc} publisher.
|
||||
% end
|
||||
/// - Returns: A publisher that emits an event when any upstream publisher emits
|
||||
/// an event.
|
||||
${declare_merge_method(arity - 1, arity, 4)}
|
||||
{
|
||||
return .init(self, ${', '.join(argument_names)})
|
||||
}
|
||||
% end
|
||||
}
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Combines elements from this publisher with those from another publisher of
|
||||
/// the same type, delivering an interleaved sequence of elements.
|
||||
///
|
||||
/// - Parameter other: Another publisher of this publisher's type.
|
||||
/// - Returns: A publisher that emits an event when either upstream publisher emits
|
||||
/// an event.
|
||||
public func merge(with other: Self) -> Publishers.MergeMany<Self> {
|
||||
return .init([self, other])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Merge 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 = \
|
||||
% (',\n' + (19 + len(publisher_name)) * ' ').join(upstream_generic_params)
|
||||
%
|
||||
% 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 created by applying the merge function to ${cardinal} upstream
|
||||
/// publishers.
|
||||
public struct ${publisher_name}<${cs_upstream_generic_params}>: Publisher
|
||||
where ${cs_upstream_generic_constraints}
|
||||
{
|
||||
public typealias Output = ${upstream_types[0]}.Output
|
||||
|
||||
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 ${upstream_types[0]}.Failure == Downstream.Failure,
|
||||
${upstream_types[0]}.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: ${arity})
|
||||
% for i in range(len(self_fields)):
|
||||
${self_fields[i]}.subscribe(Merged.Side(index: ${i}, merger: merged))
|
||||
% end
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
% for i in range(len(instantiations) + 1 - arity):
|
||||
% argument_names = ['other'] \
|
||||
% if i == 0 else [upstream_type.lower() \
|
||||
% for upstream_type in make_upstream_types_reversed(i + 1)]
|
||||
%
|
||||
|
||||
${declare_merge_method(i + 1, arity + i + 1, 8)}
|
||||
{
|
||||
return .init(${', '.join(self_fields + argument_names)})
|
||||
}
|
||||
% end
|
||||
}
|
||||
% end
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
public struct MergeMany<Upstream: Publisher>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
public let publishers: [Upstream]
|
||||
|
||||
public init(_ upstream: Upstream...) {
|
||||
self.publishers = upstream
|
||||
}
|
||||
|
||||
public init<UpstreamPublishers: Swift.Sequence>(_ upstream: UpstreamPublishers)
|
||||
where Upstream == UpstreamPublishers.Element
|
||||
{
|
||||
publishers = Array(upstream)
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
typealias Merged = _Merged<Output, Failure, Downstream>
|
||||
let merged = Merged(downstream: subscriber, count: publishers.count)
|
||||
for (i, upstream) in publishers.enumerated() {
|
||||
upstream.subscribe(Merged.Side(index: i, merger: merged))
|
||||
}
|
||||
subscriber.receive(subscription: merged)
|
||||
}
|
||||
|
||||
public func merge(with other: Upstream) -> Publishers.MergeMany<Upstream> {
|
||||
var newPublishers = publishers
|
||||
newPublishers.append(other)
|
||||
return .init(newPublishers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable conformances
|
||||
% for arity, cardinal, _ 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
|
||||
|
||||
extension Publishers.MergeMany: Equatable
|
||||
where
|
||||
Upstream: Equatable {}
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 14.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Applies a closure to create a subject that delivers elements to subscribers.
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 24.10.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Republishes elements up to the specified maximum count.
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 16.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Prints log messages for all publishing events.
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
//
|
||||
// Publishers.ReceiveOn.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 02.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
/// Specifies the scheduler on which to receive elements from the publisher.
|
||||
///
|
||||
/// You use the `receive(on:options:)` operator to receive results on a specific
|
||||
/// scheduler, such as performing UI work on the main run loop.
|
||||
/// In contrast with `subscribe(on:options:)`, which affects upstream messages,
|
||||
/// `receive(on:options:)` changes the execution context of downstream messages.
|
||||
/// In the following example, requests to `jsonPublisher` are performed on
|
||||
/// `backgroundQueue`, but elements received from it are performed on `RunLoop.main`.
|
||||
///
|
||||
/// // Some publisher.
|
||||
/// let jsonPublisher = MyJSONLoaderPublisher()
|
||||
///
|
||||
/// // Some subscriber that updates the UI.
|
||||
/// let labelUpdater = MyLabelUpdateSubscriber()
|
||||
///
|
||||
/// jsonPublisher
|
||||
/// .subscribe(on: backgroundQueue)
|
||||
/// .receiveOn(on: RunLoop.main)
|
||||
/// .subscribe(labelUpdater)
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - scheduler: The scheduler the publisher is to use for element delivery.
|
||||
/// - options: Scheduler options that customize the element delivery.
|
||||
/// - Returns: A publisher that delivers elements using the specified scheduler.
|
||||
public func receive<Context: Scheduler>(
|
||||
on scheduler: Context,
|
||||
options: Context.SchedulerOptions? = nil
|
||||
) -> Publishers.ReceiveOn<Self, Context> {
|
||||
return .init(upstream: self, scheduler: scheduler, options: options)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that delivers elements to its downstream subscriber on a specific
|
||||
/// scheduler.
|
||||
public struct ReceiveOn<Upstream: Publisher, Context: Scheduler>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// The scheduler the publisher is to use for element delivery.
|
||||
public let scheduler: Context
|
||||
|
||||
/// Scheduler options that customize the delivery of elements.
|
||||
public let options: Context.SchedulerOptions?
|
||||
|
||||
public init(upstream: Upstream,
|
||||
scheduler: Context,
|
||||
options: Context.SchedulerOptions?) {
|
||||
self.upstream = upstream
|
||||
self.scheduler = scheduler
|
||||
self.options = options
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
upstream.subscribe(Inner(self, downstream: subscriber))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.ReceiveOn {
|
||||
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
|
||||
|
||||
typealias ReceiveOn = Publishers.ReceiveOn<Upstream, Context>
|
||||
|
||||
private enum State {
|
||||
case ready(ReceiveOn, Downstream)
|
||||
case subscribed(ReceiveOn, Downstream, Subscription)
|
||||
case terminal
|
||||
}
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
private var state: State
|
||||
private let downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
init(_ receiveOn: ReceiveOn, downstream: Downstream) {
|
||||
state = .ready(receiveOn, downstream)
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case let .ready(receiveOn, downstream) = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .subscribed(receiveOn, downstream, subscription)
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
downstream.receive(subscription: self)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
func receive(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
guard case let .subscribed(receiveOn, downstream, _) = state else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
lock.unlock()
|
||||
receiveOn.scheduler.schedule(options: receiveOn.options) { [weak self] in
|
||||
self?.scheduledReceive(input, downstream: downstream)
|
||||
}
|
||||
return .none
|
||||
}
|
||||
|
||||
private func scheduledReceive(_ input: Upstream.Output, downstream: Downstream) {
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(input)
|
||||
downstreamLock.unlock()
|
||||
guard newDemand > 0 else {
|
||||
return
|
||||
}
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscription.request(newDemand)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(receiveOn, downstream, _) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
receiveOn.scheduler.schedule(options: receiveOn.options) { [weak self] in
|
||||
self?.scheduledReceive(completion: completion, downstream: downstream)
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduledReceive(completion: Subscribers.Completion<Failure>,
|
||||
downstream: Downstream) {
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "ReceiveOn" }
|
||||
|
||||
var customMirror: Mirror { return Mirror(self, children: EmptyCollection()) }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
//
|
||||
// Publishers.ReplaceEmpty.swift
|
||||
// OpenCombine
|
||||
//
|
||||
// Created by Joe Spadafora on 12/10/19.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Replaces an empty stream with the provided element.
|
||||
///
|
||||
/// If the upstream publisher finishes without producing any elements,
|
||||
/// this publisher emits the provided element, then finishes normally.
|
||||
/// - Parameter output: An element to emit when the upstream publisher
|
||||
/// finishes without emitting any elements.
|
||||
/// - Returns: A publisher that replaces an empty stream with
|
||||
/// the provided output element.
|
||||
public func replaceEmpty(with output: Output) -> Publishers.ReplaceEmpty<Self> {
|
||||
return .init(upstream: self, output: output)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that replaces an empty stream with a provided element.
|
||||
public struct ReplaceEmpty<Upstream: Publisher>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The element to deliver when the upstream publisher finishes
|
||||
/// without delivering any elements.
|
||||
public let output: Upstream.Output
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
public init(upstream: Upstream, output: Output) {
|
||||
self.upstream = upstream
|
||||
self.output = output
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
let inner = Inner(downstream: subscriber, output: output)
|
||||
upstream.subscribe(inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.ReplaceEmpty: Equatable
|
||||
where Upstream: Equatable, Upstream.Output: Equatable {}
|
||||
|
||||
extension Publishers.ReplaceEmpty {
|
||||
|
||||
private final class Inner<Downstream: Subscriber>
|
||||
: Subscriber,
|
||||
Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
|
||||
typealias Input = Upstream.Output
|
||||
typealias Failure = Upstream.Failure
|
||||
|
||||
private let output: Output
|
||||
private let downstream: Downstream
|
||||
|
||||
private var receivedUpstream = false
|
||||
private var lock = UnfairLock.allocate()
|
||||
private var downstreamRequested = false
|
||||
private var finishedWithoutUpstream = false
|
||||
|
||||
private var status = SubscriptionStatus.awaitingSubscription
|
||||
|
||||
fileprivate init(downstream: Downstream, output: Output) {
|
||||
self.downstream = downstream
|
||||
self.output = output
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case .awaitingSubscription = status else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
status = .subscribed(subscription)
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
subscription.request(.unlimited)
|
||||
}
|
||||
|
||||
func receive(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
guard case .subscribed = status else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
receivedUpstream = true
|
||||
lock.unlock()
|
||||
return downstream.receive(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
lock.lock()
|
||||
guard case .subscribed = status else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
status = .terminal
|
||||
if receivedUpstream {
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
return
|
||||
}
|
||||
switch completion {
|
||||
case .finished:
|
||||
if downstreamRequested {
|
||||
lock.unlock()
|
||||
_ = downstream.receive(output)
|
||||
downstream.receive(completion: completion)
|
||||
return
|
||||
}
|
||||
finishedWithoutUpstream = true
|
||||
lock.unlock()
|
||||
case .failure:
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
downstreamRequested = true
|
||||
if finishedWithoutUpstream {
|
||||
lock.unlock()
|
||||
_ = downstream.receive(output)
|
||||
downstream.receive(completion: .finished)
|
||||
return
|
||||
}
|
||||
guard case let .subscribed(subscription) = status else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscription.request(demand)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard case let .subscribed(subscription) = status else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
status = .terminal
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
var description: String { return "ReplaceEmpty" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
return Mirror(self, children: EmptyCollection())
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Bogdan Vlad on 8/29/19.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
/// Replaces any errors in the stream with the provided element.
|
||||
///
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// Created by Eric Patey on 26.08.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Transforms elements from the upstream publisher by providing the current element
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 19.06.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that publishes a given sequence of elements.
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
//
|
||||
// Publishers.SubscribeOn.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 02.12.2019.
|
||||
//
|
||||
|
||||
extension Publisher {
|
||||
|
||||
/// Specifies the scheduler on which to perform subscribe, cancel, and request
|
||||
/// operations.
|
||||
///
|
||||
/// In contrast with `receive(on:options:)`, which affects downstream messages,
|
||||
/// `subscribe(on:)` changes the execution context of upstream messages.
|
||||
/// In the following example, requests to `jsonPublisher` are performed on
|
||||
/// `backgroundQueue`, but elements received from it are performed on `RunLoop.main`.
|
||||
///
|
||||
/// let ioPerformingPublisher == // Some publisher.
|
||||
/// let uiUpdatingSubscriber == // Some subscriber that updates the UI.
|
||||
///
|
||||
/// ioPerformingPublisher
|
||||
/// .subscribe(on: backgroundQueue)
|
||||
/// .receiveOn(on: RunLoop.main)
|
||||
/// .subscribe(uiUpdatingSubscriber)
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - scheduler: The scheduler on which to receive upstream messages.
|
||||
/// - options: Options that customize the delivery of elements.
|
||||
/// - Returns: A publisher which performs upstream operations on the specified
|
||||
/// scheduler.
|
||||
public func subscribe<Context: Scheduler>(
|
||||
on scheduler: Context,
|
||||
options: Context.SchedulerOptions? = nil
|
||||
) -> Publishers.SubscribeOn<Self, Context> {
|
||||
return .init(upstream: self, scheduler: scheduler, options: options)
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers {
|
||||
|
||||
/// A publisher that receives elements from an upstream publisher on a specific
|
||||
/// scheduler.
|
||||
public struct SubscribeOn<Upstream: Publisher, Context: Scheduler>: Publisher {
|
||||
|
||||
public typealias Output = Upstream.Output
|
||||
|
||||
public typealias Failure = Upstream.Failure
|
||||
|
||||
/// The publisher from which this publisher receives elements.
|
||||
public let upstream: Upstream
|
||||
|
||||
/// The scheduler the publisher should use to receive elements.
|
||||
public let scheduler: Context
|
||||
|
||||
/// Scheduler options that customize the delivery of elements.
|
||||
public let options: Context.SchedulerOptions?
|
||||
|
||||
public init(upstream: Upstream,
|
||||
scheduler: Context,
|
||||
options: Context.SchedulerOptions?) {
|
||||
self.upstream = upstream
|
||||
self.scheduler = scheduler
|
||||
self.options = options
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Upstream.Failure == Downstream.Failure,
|
||||
Upstream.Output == Downstream.Input
|
||||
{
|
||||
scheduler.schedule(options: options) {
|
||||
self.upstream.subscribe(Inner(self, downstream: subscriber))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers.SubscribeOn {
|
||||
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
|
||||
|
||||
typealias SubscribeOn = Publishers.SubscribeOn<Upstream, Context>
|
||||
|
||||
private enum State {
|
||||
case ready(SubscribeOn, Downstream)
|
||||
case subscribed(SubscribeOn, Downstream, Subscription)
|
||||
case terminal
|
||||
}
|
||||
|
||||
private let lock = UnfairLock.allocate()
|
||||
private var state: State
|
||||
private let upstreamLock = UnfairLock.allocate()
|
||||
|
||||
init(_ subscribeOn: SubscribeOn, downstream: Downstream) {
|
||||
state = .ready(subscribeOn, downstream)
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
upstreamLock.deallocate()
|
||||
}
|
||||
|
||||
func receive(subscription: Subscription) {
|
||||
lock.lock()
|
||||
guard case let .ready(subscribeOn, downstream) = state else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
state = .subscribed(subscribeOn, downstream, subscription)
|
||||
lock.unlock()
|
||||
downstream.receive(subscription: self)
|
||||
}
|
||||
|
||||
func receive(_ input: Upstream.Output) -> Subscribers.Demand {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, downstream, _) = state else {
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
lock.unlock()
|
||||
return downstream.receive(input)
|
||||
}
|
||||
|
||||
func receive(completion: Subscribers.Completion<Upstream.Failure>) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(_, downstream, _) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
downstream.receive(completion: completion)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
guard case let .subscribed(subscribeOn, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lock.unlock()
|
||||
subscribeOn.scheduler.schedule(options: subscribeOn.options) { [weak self] in
|
||||
self?.scheduledRequest(demand, subscription: subscription)
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduledRequest(_ demand: Subscribers.Demand,
|
||||
subscription: Subscription) {
|
||||
upstreamLock.lock()
|
||||
subscription.request(demand)
|
||||
upstreamLock.unlock()
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard case let .subscribed(subscribeOn, _, subscription) = state else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
state = .terminal
|
||||
lock.unlock()
|
||||
subscribeOn.scheduler.schedule(options: subscribeOn.options) { [weak self] in
|
||||
self?.scheduledCancel(subscription)
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduledCancel(_ subscription: Subscription) {
|
||||
upstreamLock.lock()
|
||||
subscription.cancel()
|
||||
upstreamLock.unlock()
|
||||
}
|
||||
|
||||
var description: String { return "SubscribeOn" }
|
||||
|
||||
var customMirror: Mirror { return Mirror(self, children: EmptyCollection()) }
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
//
|
||||
// Publishers._Merged.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 03.12.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
extension Publishers {
|
||||
// swiftlint:disable:next type_name
|
||||
internal final class _Merged<Input, Failure, Downstream: Subscriber>
|
||||
: Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Input, Downstream.Failure == Failure
|
||||
{
|
||||
private let downstream: Downstream
|
||||
private var demand = Subscribers.Demand.none // 0x78
|
||||
private var terminated = false // 0x80
|
||||
private let count: Int // 0x88
|
||||
private var upstreamFinished = 0 // 0x90
|
||||
private var finished = false // 0x98
|
||||
|
||||
// TODO: The size of these arrays always stays the same.
|
||||
// Maybe we can leverage ManagedBuffer/ManagedBufferPointer here
|
||||
// to avoid additional allocations.
|
||||
private var subscriptions: [Subscription?] // 0xA0
|
||||
private var buffers: [Input?] // 0xA8
|
||||
|
||||
private let lock = UnfairLock.allocate() // 0xB0
|
||||
private let downstreamLock = UnfairLock.allocate() // 0xB8
|
||||
private var recursive = false // 0xC0
|
||||
private var pending = Subscribers.Demand.none // 0xC8
|
||||
|
||||
internal init(downstream: Downstream, count: Int) {
|
||||
self.downstream = downstream
|
||||
self.count = count
|
||||
self.subscriptions = Array(repeating: nil, count: count)
|
||||
self.buffers = Array(repeating: nil, count: count)
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
private func receive(subscription: Subscription, _ index: Int) {
|
||||
lock.lock()
|
||||
guard subscriptions[index] == nil else {
|
||||
lock.unlock()
|
||||
subscription.cancel()
|
||||
return
|
||||
}
|
||||
subscriptions[index] = subscription
|
||||
let demand = self.demand
|
||||
lock.unlock()
|
||||
subscription.request(demand == .unlimited ? .unlimited : .max(1))
|
||||
}
|
||||
|
||||
private func receive(_ input: Input, _ index: Int) -> Subscribers.Demand {
|
||||
func lockedSendValueDownstream() -> Subscribers.Demand {
|
||||
recursive = true
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(input)
|
||||
downstreamLock.unlock()
|
||||
lock.lock()
|
||||
recursive = false
|
||||
return newDemand
|
||||
}
|
||||
|
||||
lock.lock()
|
||||
if demand == .unlimited {
|
||||
let newDemand = lockedSendValueDownstream()
|
||||
lock.unlock()
|
||||
return newDemand
|
||||
}
|
||||
if demand == .none {
|
||||
buffers[index] = input
|
||||
lock.unlock()
|
||||
return .none
|
||||
}
|
||||
demand -= 1
|
||||
let newDemand = lockedSendValueDownstream()
|
||||
demand += newDemand + pending
|
||||
pending = .none
|
||||
lock.unlock()
|
||||
return .max(1)
|
||||
}
|
||||
|
||||
private func receive(completion: Subscribers.Completion<Failure>, _ index: Int) {
|
||||
func lockedSendCompletionDownstream() {
|
||||
recursive = true
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
downstream.receive(completion: completion)
|
||||
downstreamLock.unlock()
|
||||
lock.lock()
|
||||
recursive = false
|
||||
}
|
||||
|
||||
lock.lock()
|
||||
switch completion {
|
||||
case .finished:
|
||||
upstreamFinished += 1
|
||||
subscriptions[index] = nil
|
||||
// TODO: Test both conditions.
|
||||
// When receiving subscription twice, the second time
|
||||
// upstreamFinished != count
|
||||
guard upstreamFinished == count,
|
||||
subscriptions.allSatisfy({ $0 == nil }) else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
finished = true
|
||||
lockedSendCompletionDownstream()
|
||||
lock.unlock()
|
||||
case .failure:
|
||||
if terminated {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
terminated = true
|
||||
let subscriptions = self.subscriptions
|
||||
self.subscriptions = Array(repeating: nil, count: subscriptions.count)
|
||||
lock.unlock()
|
||||
for (i, subscription) in subscriptions.enumerated() where i != index {
|
||||
subscription?.cancel()
|
||||
}
|
||||
lock.lock()
|
||||
lockedSendCompletionDownstream()
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
internal func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
// TODO: Test all conditions
|
||||
if terminated || finished || demand == .none || self.demand == .unlimited {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
if recursive {
|
||||
pending += demand
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
if demand == .unlimited {
|
||||
// loc_6a5b1
|
||||
self.demand = .unlimited
|
||||
}
|
||||
|
||||
// TODO: Unimplemented
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
internal func cancel() {
|
||||
// TODO: Unimplemented
|
||||
}
|
||||
|
||||
internal var description: String { return "Merge" }
|
||||
|
||||
internal var customMirror: Mirror {
|
||||
return Mirror(self, children: EmptyCollection())
|
||||
}
|
||||
|
||||
internal var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
|
||||
extension Publishers._Merged {
|
||||
internal struct Side
|
||||
: Subscriber,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
{
|
||||
private let index: Int
|
||||
private let merger: Publishers._Merged<Input, Failure, Downstream>
|
||||
|
||||
internal let combineIdentifier = CombineIdentifier()
|
||||
|
||||
internal init(index: Int,
|
||||
merger: Publishers._Merged<Input, Failure, Downstream>) {
|
||||
self.index = index
|
||||
self.merger = merger
|
||||
}
|
||||
|
||||
internal func receive(subscription: Subscription) {
|
||||
merger.receive(subscription: subscription, index)
|
||||
}
|
||||
|
||||
internal func receive(_ input: Input) -> Subscribers.Demand {
|
||||
return merger.receive(input, index)
|
||||
}
|
||||
|
||||
internal func receive(completion: Subscribers.Completion<Failure>) {
|
||||
merger.receive(completion: completion, index)
|
||||
}
|
||||
|
||||
internal var description: String { return "Merge" }
|
||||
|
||||
internal var customMirror: Mirror {
|
||||
let children = CollectionOfOne<Mirror.Child>(
|
||||
("parentSubscription", merger.combineIdentifier)
|
||||
)
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
internal var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
// Created by Sergej Jaskiewicz on 12.11.2019.
|
||||
//
|
||||
|
||||
import COpenCombineHelpers
|
||||
|
||||
/// A publisher that allows for recording a series of inputs and a completion for later
|
||||
/// playback to each subscriber.
|
||||
public struct Record<Output, Failure: Error>: Publisher {
|
||||
|
||||
@@ -330,7 +330,6 @@ extension Subscribers {
|
||||
|
||||
/// Returns `true` if `lhs` and `rhs` are not equal. `.unlimited` is not equal to
|
||||
/// any integer.
|
||||
@inlinable
|
||||
public static func != (lhs: Demand, rhs: Int) -> Bool {
|
||||
if lhs == .unlimited {
|
||||
return true
|
||||
@@ -341,7 +340,6 @@ extension Subscribers {
|
||||
|
||||
/// Returns `true` if `lhs` and `rhs` are equal. `.unlimited` is not equal to any
|
||||
/// integer.
|
||||
@inlinable
|
||||
public static func == (lhs: Int, rhs: Demand) -> Bool {
|
||||
if rhs == .unlimited {
|
||||
return false
|
||||
@@ -352,7 +350,6 @@ extension Subscribers {
|
||||
|
||||
/// Returns `true` if `lhs` and `rhs` are not equal. `.unlimited` is not equal to
|
||||
/// any integer.
|
||||
@inlinable
|
||||
public static func != (lhs: Int, rhs: Demand) -> Bool {
|
||||
if rhs == .unlimited {
|
||||
return true
|
||||
@@ -361,13 +358,8 @@ extension Subscribers {
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public static func == (lhs: Demand, rhs: Demand) -> Bool {
|
||||
return lhs.rawValue == rhs.rawValue
|
||||
}
|
||||
|
||||
/// Returns the number of requested values, or `nil` if `.unlimited`.
|
||||
@inlinable public var max: Int? {
|
||||
public var max: Int? {
|
||||
if self == .unlimited {
|
||||
return nil
|
||||
} else {
|
||||
|
||||
@@ -50,12 +50,8 @@ extension DispatchQueue {
|
||||
/// - Parameter other: Another dispatch queue time.
|
||||
/// - Returns: The time interval between this time and the provided time.
|
||||
public func distance(to other: SchedulerTimeType) -> Stride {
|
||||
let start = dispatchTime.rawValue
|
||||
let end = other.dispatchTime.rawValue
|
||||
return .nanoseconds(
|
||||
end >= start
|
||||
? Int(Int64(bitPattern: end) - Int64(bitPattern: start))
|
||||
: -Int(Int64(bitPattern: start) - Int64(bitPattern: end))
|
||||
dispatchTime.rawValue.distance(to: other.dispatchTime.rawValue)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -66,9 +62,7 @@ extension DispatchQueue {
|
||||
/// - Returns: A dispatch queue time advanced by the given
|
||||
/// interval from this instance’s time.
|
||||
public func advanced(by stride: Stride) -> SchedulerTimeType {
|
||||
return stride.magnitude == .max
|
||||
? .init(.distantFuture)
|
||||
: .init(dispatchTime + stride.timeInterval)
|
||||
return .init(dispatchTime + stride.timeInterval)
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
@@ -131,52 +125,13 @@ extension DispatchQueue {
|
||||
self = .microseconds(microseconds)
|
||||
case .nanoseconds(let nanoseconds):
|
||||
self = .nanoseconds(nanoseconds)
|
||||
// This dance is to avoid the warning 'default will never be executed'
|
||||
// on non-Darwin platforms.
|
||||
// There really shouldn't be a warning.
|
||||
// See https://forums.swift.org/t/unknown-default-produces-a-warning-on-linux-with-non-frozen-enum/31687
|
||||
//
|
||||
// Thanks to Jeremy David Giesbrecht for suggesting this workaround.
|
||||
#if canImport(Darwin)
|
||||
case .never:
|
||||
self = .nanoseconds(.max)
|
||||
fallthrough
|
||||
@unknown default:
|
||||
self.init(__guessFromUnknown: timeInterval)
|
||||
#else
|
||||
default:
|
||||
if case .never = timeInterval {
|
||||
self = .nanoseconds(.max)
|
||||
} else {
|
||||
self.init(__guessFromUnknown: timeInterval)
|
||||
}
|
||||
#endif
|
||||
self = .nanoseconds(.max)
|
||||
}
|
||||
}
|
||||
|
||||
public // testable
|
||||
init(__guessFromUnknown timeInterval: DispatchTimeInterval) {
|
||||
// Let's take some reference time,
|
||||
// add `timeInterval` to it, take the `rawValue` from the result
|
||||
// and subtract the `rawValue` of the reference time.
|
||||
//
|
||||
// We won't be able to provide the exact implementation though,
|
||||
// because something will definitely overflow.
|
||||
//
|
||||
// However, we can try to support as wide a range of values
|
||||
// as possible.
|
||||
//
|
||||
// By trial and error I got that the `rawValue` of `UInt64.max / 13`
|
||||
// gives us propably the widest range of supported values:
|
||||
// from `Int.min / 6.5` to `Int.max / 2.889` nanoseconds.
|
||||
// That's with Int being 64 bits. Since here only UInt64 can overflow,
|
||||
// when Int is 32 bits, we don't have this issue.
|
||||
// It should be more than enough.
|
||||
|
||||
let referenceTime = DispatchTime(uptimeNanoseconds: .max / 13)
|
||||
self = SchedulerTimeType(referenceTime)
|
||||
.distance(to: SchedulerTimeType(referenceTime + timeInterval))
|
||||
}
|
||||
|
||||
/// Creates a dispatch queue time interval from a floating-point
|
||||
/// seconds value.
|
||||
///
|
||||
@@ -236,15 +191,15 @@ extension DispatchQueue {
|
||||
}
|
||||
|
||||
public static func seconds(_ value: Int) -> Stride {
|
||||
return Stride(magnitude: clampedIntProduct(value, 1_000_000_000))
|
||||
return Stride(magnitude: value * 1_000_000_000)
|
||||
}
|
||||
|
||||
public static func milliseconds(_ value: Int) -> Stride {
|
||||
return Stride(magnitude: clampedIntProduct(value, 1_000_000))
|
||||
return Stride(magnitude: value * 1_000_000)
|
||||
}
|
||||
|
||||
public static func microseconds(_ value: Int) -> Stride {
|
||||
return Stride(magnitude: clampedIntProduct(value, 1_000))
|
||||
return Stride(magnitude: value * 1_000)
|
||||
}
|
||||
|
||||
public static func nanoseconds(_ value: Int) -> Stride {
|
||||
@@ -381,18 +336,3 @@ extension DispatchQueue: OpenCombine.Scheduler {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// This function is taken from swift-corlibs-libdispatch:
|
||||
// https://github.com/apple/swift-corelibs-libdispatch/blob/c992dacf3ca114806e6ac9ffc9113b19255be9fe/src/swift/Time.swift#L134-L144
|
||||
//
|
||||
// Returns m1 * m2, clamped to the range [Int.min, Int.max].
|
||||
// Because of the way this function is used, we can always assume
|
||||
// that m2 > 0.
|
||||
private func clampedIntProduct(_ lhs: Int, _ rhs: Int) -> Int {
|
||||
assert(rhs > 0, "multiplier must be positive")
|
||||
let (result, overflow) = lhs.multipliedReportingOverflow(by: rhs)
|
||||
if overflow {
|
||||
return lhs > 0 ? .max : .min
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// Locking.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
#if canImport(COpenCombineHelpers)
|
||||
import COpenCombineHelpers
|
||||
#endif
|
||||
|
||||
import OpenCombine
|
||||
|
||||
internal typealias UnfairLock = __UnfairLock
|
||||
internal typealias UnfairRecursiveLock = __UnfairRecursiveLock
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// Violations.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 13.12.2019.
|
||||
//
|
||||
|
||||
import OpenCombine
|
||||
|
||||
extension Subscribers.Demand {
|
||||
internal func assertNonZero(file: StaticString = #file,
|
||||
line: UInt = #line) {
|
||||
if self == .none {
|
||||
fatalError("API Violation: demand must not be zero", file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// JSONEncoder.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.10.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OpenCombine
|
||||
|
||||
extension JSONEncoder: TopLevelEncoder {
|
||||
public typealias Output = Data
|
||||
}
|
||||
|
||||
extension JSONDecoder: TopLevelDecoder {
|
||||
public typealias Input = Data
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
//
|
||||
// NotificationCenter.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.10.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OpenCombine
|
||||
|
||||
extension NotificationCenter {
|
||||
|
||||
/// A namespace for disambiguation when both OpenCombine and Foundation are imported.
|
||||
///
|
||||
/// Foundation extends `NotificationCenter` with new methods and nested types.
|
||||
/// If you import both OpenCombine and Foundation, you will not be able
|
||||
/// to write `NotificationCenter.Publisher`,
|
||||
/// because Swift is unable to understand which `Publisher`
|
||||
/// you're referring to — the one declared in Foundation or in OpenCombine.
|
||||
///
|
||||
/// So you have to write `NotificationCenter.OCombine.Publisher`.
|
||||
///
|
||||
/// This bug is tracked [here](https://bugs.swift.org/browse/SR-11183).
|
||||
///
|
||||
/// You can omit this whenever Combine is not available (e. g. on Linux).
|
||||
public struct OCombine {
|
||||
|
||||
public let center: NotificationCenter
|
||||
|
||||
public init(_ center: NotificationCenter) {
|
||||
self.center = center
|
||||
}
|
||||
|
||||
/// A publisher that emits elements when broadcasting notifications.
|
||||
public struct Publisher: OpenCombine.Publisher {
|
||||
|
||||
public typealias Output = Notification
|
||||
|
||||
public typealias Failure = Never
|
||||
|
||||
/// The notification center this publisher uses as a source.
|
||||
public let center: NotificationCenter
|
||||
|
||||
/// The name of notifications published by this publisher.
|
||||
public let name: Notification.Name
|
||||
|
||||
/// The object posting the named notfication.
|
||||
public let object: AnyObject?
|
||||
|
||||
/// Creates a publisher that emits events when broadcasting notifications.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - center: The notification center to publish notifications for.
|
||||
/// - name: The name of the notification to publish.
|
||||
/// - object: The object posting the named notfication. If `nil`,
|
||||
/// the publisher emits elements for any object producing a notification
|
||||
/// with the given name.
|
||||
public init(center: NotificationCenter,
|
||||
name: Notification.Name,
|
||||
object: AnyObject? = nil) {
|
||||
self.center = center
|
||||
self.name = name
|
||||
self.object = object
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Failure == Never, Downstream.Input == Notification
|
||||
{
|
||||
let subscription = Notification.Subscription(center: center,
|
||||
name: name,
|
||||
object: object,
|
||||
downstream: subscriber)
|
||||
subscriber.receive(subscription: subscription)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a publisher that emits events when broadcasting notifications.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - name: The name of the notification to publish.
|
||||
/// - object: The object posting the named notfication. If `nil`, the publisher
|
||||
/// emits elements for any object producing a notification with the given
|
||||
/// name.
|
||||
/// - Returns: A publisher that emits events when broadcasting notifications.
|
||||
public func publisher(for name: Notification.Name,
|
||||
object: AnyObject? = nil) -> Publisher {
|
||||
return .init(center: center, name: name, object: object)
|
||||
}
|
||||
}
|
||||
|
||||
#if !canImport(Combine)
|
||||
/// A publisher that emits elements when broadcasting notifications.
|
||||
public typealias Publisher = OCombine.Publisher
|
||||
#endif
|
||||
}
|
||||
|
||||
extension NotificationCenter {
|
||||
|
||||
/// A namespace for disambiguation when both OpenCombine and Foundation are imported.
|
||||
///
|
||||
/// Foundation extends `NotificationCenter` with new methods and nested types.
|
||||
/// If you import both OpenCombine and Foundation, you will not be able
|
||||
/// to write `NotificationCenter.default.publisher(for: name)`,
|
||||
/// because Swift is unable to understand which `publisher` method
|
||||
/// you're referring to — the one declared in Foundation or in OpenCombine.
|
||||
///
|
||||
/// So you have to write `NotificationCenter.default.ocombine.publisher(for: name)`.
|
||||
///
|
||||
/// This bug is tracked [here](https://bugs.swift.org/browse/SR-11183).
|
||||
///
|
||||
/// You can omit this whenever Combine is not available (e. g. on Linux).
|
||||
public var ocombine: OCombine { return .init(self) }
|
||||
|
||||
#if !canImport(Combine)
|
||||
/// Returns a publisher that emits events when broadcasting notifications.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - name: The name of the notification to publish.
|
||||
/// - object: The object posting the named notfication. If `nil`, the publisher
|
||||
/// emits elements for any object producing a notification with the given name.
|
||||
/// - Returns: A publisher that emits events when broadcasting notifications.
|
||||
public func publisher(for name: Notification.Name,
|
||||
object: AnyObject? = nil) -> OCombine.Publisher {
|
||||
return ocombine.publisher(for: name, object: object)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extension NotificationCenter.OCombine.Publisher: Equatable {
|
||||
public static func == (lhs: NotificationCenter.OCombine.Publisher,
|
||||
rhs: NotificationCenter.OCombine.Publisher) -> Bool {
|
||||
return lhs.center == rhs.center &&
|
||||
lhs.name == rhs.name &&
|
||||
lhs.object === rhs.object
|
||||
}
|
||||
}
|
||||
|
||||
extension Notification {
|
||||
fileprivate final class Subscription<Downstream: Subscriber>
|
||||
: OpenCombine.Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == Notification, Downstream.Failure == Never
|
||||
{
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
fileprivate let downstreamLock = UnfairRecursiveLock.allocate()
|
||||
|
||||
fileprivate var demand = Subscribers.Demand.none
|
||||
|
||||
private var center: NotificationCenter?
|
||||
|
||||
private let name: Name
|
||||
|
||||
private var object: AnyObject?
|
||||
|
||||
private var observation: AnyObject?
|
||||
|
||||
fileprivate init(center: NotificationCenter,
|
||||
name: Notification.Name,
|
||||
object: AnyObject?,
|
||||
downstream: Downstream) {
|
||||
self.center = center
|
||||
self.name = name
|
||||
self.object = object
|
||||
self.observation = center
|
||||
.addObserver(forName: name, object: object, queue: nil) { [weak self] in
|
||||
self?.didReceiveNotification($0, downstream: downstream)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
downstreamLock.deallocate()
|
||||
}
|
||||
|
||||
private func didReceiveNotification(_ notification: Notification,
|
||||
downstream: Downstream) {
|
||||
lock.lock()
|
||||
guard demand > 0 else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
demand -= 1
|
||||
lock.unlock()
|
||||
downstreamLock.lock()
|
||||
let newDemand = downstream.receive(notification)
|
||||
downstreamLock.unlock()
|
||||
lock.lock()
|
||||
demand += newDemand
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
lock.lock()
|
||||
self.demand += demand
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard let center = self.center, let observation = self.observation else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
self.center = nil
|
||||
self.object = nil
|
||||
self.observation = nil
|
||||
lock.unlock()
|
||||
center.removeObserver(observation)
|
||||
}
|
||||
|
||||
fileprivate var description: String { return "NotificationCenter Observer" }
|
||||
|
||||
fileprivate var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("center", center as Any),
|
||||
("name", name),
|
||||
("object", object as Any),
|
||||
("demand", demand)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
fileprivate var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// PropertyListEncoder.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OpenCombine
|
||||
|
||||
extension PropertyListEncoder: TopLevelEncoder {
|
||||
public typealias Output = Data
|
||||
}
|
||||
|
||||
extension PropertyListDecoder: TopLevelDecoder {
|
||||
public typealias Input = Data
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
//
|
||||
// URLSession.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 13.12.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
#if canImport(FoundationNetworking)
|
||||
import FoundationNetworking
|
||||
#endif
|
||||
|
||||
import OpenCombine
|
||||
|
||||
extension URLSession {
|
||||
|
||||
/// A namespace for disambiguation when both OpenCombine and Foundation are imported.
|
||||
///
|
||||
/// Foundation extends `URLSession` with new methods and nested types.
|
||||
/// If you import both OpenCombine and Foundation, you will not be able
|
||||
/// to write `URLSession.DataTaskPublisher`,
|
||||
/// because Swift is unable to understand which `DataTaskPublisher`
|
||||
/// you're referring to — the one declared in Foundation or in OpenCombine.
|
||||
///
|
||||
/// So you have to write `URLSession.OCombine.DataTaskPublisher`.
|
||||
///
|
||||
/// This bug is tracked [here](https://bugs.swift.org/browse/SR-11183).
|
||||
///
|
||||
/// You can omit this whenever Combine is not available (e. g. on Linux).
|
||||
public struct OCombine {
|
||||
|
||||
public let session: URLSession
|
||||
|
||||
public init(_ session: URLSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
public struct DataTaskPublisher: Publisher {
|
||||
|
||||
public typealias Output = (data: Data, response: URLResponse)
|
||||
|
||||
public typealias Failure = URLError
|
||||
|
||||
public let request: URLRequest
|
||||
|
||||
public let session: URLSession
|
||||
|
||||
public init(request: URLRequest, session: URLSession) {
|
||||
self.request = request
|
||||
self.session = session
|
||||
}
|
||||
|
||||
public func receive<Downstream: Subscriber>(subscriber: Downstream)
|
||||
where Downstream.Failure == Failure, Downstream.Input == Output
|
||||
{
|
||||
let subscription = Inner(parent: self, downstream: subscriber)
|
||||
subscriber.receive(subscription: subscription)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a publisher that wraps a URL session data task for a given URL.
|
||||
///
|
||||
/// The publisher publishes data when the task completes, or terminates if
|
||||
/// the task fails with an error.
|
||||
///
|
||||
/// - Parameter url: The URL for which to create a data task.
|
||||
/// - Returns: A publisher that wraps a data task for the URL.
|
||||
public func dataTaskPublisher(for url: URL) -> DataTaskPublisher {
|
||||
return dataTaskPublisher(for: URLRequest(url: url))
|
||||
}
|
||||
|
||||
/// Returns a publisher that wraps a URL session data task for a given
|
||||
/// URL request.
|
||||
///
|
||||
/// The publisher publishes data when the task completes, or terminates if
|
||||
/// the task fails with an error.
|
||||
///
|
||||
/// - Parameter request: The URL request for which to create a data task.
|
||||
/// - Returns: A publisher that wraps a data task for the URL request.
|
||||
public func dataTaskPublisher(for request: URLRequest) -> DataTaskPublisher {
|
||||
return .init(request: request, session: session)
|
||||
}
|
||||
}
|
||||
|
||||
#if !canImport(Combine)
|
||||
public typealias DataTaskPublisher = OCombine.DataTaskPublisher
|
||||
#endif
|
||||
}
|
||||
|
||||
extension URLSession {
|
||||
|
||||
/// A namespace for disambiguation when both OpenCombine and Foundation are imported.
|
||||
///
|
||||
/// Foundation extends `URLSession` with new methods and nested types.
|
||||
/// If you import both OpenCombine and Foundation, you will not be able
|
||||
/// to write `URLSession.shared.dataTaskPublisher(for: url)`,
|
||||
/// because Swift is unable to understand which `dataTaskPublisher` method
|
||||
/// you're referring to — the one declared in Foundation or in OpenCombine.
|
||||
///
|
||||
/// So you have to write `URLSession.shared.ocombine.dataTaskPublisher(for: url)`.
|
||||
///
|
||||
/// This bug is tracked [here](https://bugs.swift.org/browse/SR-11183).
|
||||
///
|
||||
/// You can omit this whenever Combine is not available (e. g. on Linux).
|
||||
public var ocombine: OCombine { return .init(self) }
|
||||
|
||||
#if !canImport(Combine)
|
||||
/// Returns a publisher that wraps a URL session data task for a given URL.
|
||||
///
|
||||
/// The publisher publishes data when the task completes, or terminates if the task
|
||||
/// fails with an error.
|
||||
///
|
||||
/// - Parameter url: The URL for which to create a data task.
|
||||
/// - Returns: A publisher that wraps a data task for the URL.
|
||||
public func dataTaskPublisher(for url: URL) -> DataTaskPublisher {
|
||||
return ocombine.dataTaskPublisher(for: url)
|
||||
}
|
||||
|
||||
/// Returns a publisher that wraps a URL session data task for a given URL request.
|
||||
///
|
||||
/// The publisher publishes data when the task completes, or terminates if the task
|
||||
/// fails with an error.
|
||||
///
|
||||
/// - Parameter request: The URL request for which to create a data task.
|
||||
/// - Returns: A publisher that wraps a data task for the URL request.
|
||||
public func dataTaskPublisher(for request: URLRequest) -> DataTaskPublisher {
|
||||
return ocombine.dataTaskPublisher(for: request)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extension URLSession.OCombine.DataTaskPublisher {
|
||||
private class Inner<Downstream: Subscriber>
|
||||
: Subscription,
|
||||
CustomStringConvertible,
|
||||
CustomReflectable,
|
||||
CustomPlaygroundDisplayConvertible
|
||||
where Downstream.Input == (data: Data, response: URLResponse),
|
||||
Downstream.Failure == URLError
|
||||
{
|
||||
private let lock = UnfairLock.allocate()
|
||||
|
||||
private var parent: URLSession.OCombine.DataTaskPublisher?
|
||||
|
||||
private var downstream: Downstream?
|
||||
|
||||
private var demand = Subscribers.Demand.none
|
||||
|
||||
private var task: URLSessionDataTask?
|
||||
|
||||
fileprivate init(parent: URLSession.OCombine.DataTaskPublisher,
|
||||
downstream: Downstream) {
|
||||
self.parent = parent
|
||||
self.downstream = downstream
|
||||
}
|
||||
|
||||
deinit {
|
||||
lock.deallocate()
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
demand.assertNonZero()
|
||||
lock.lock()
|
||||
guard let parent = self.parent else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
if self.task == nil {
|
||||
task = parent.session.dataTask(with: parent.request,
|
||||
completionHandler: handleResponse)
|
||||
}
|
||||
self.demand += demand
|
||||
let task = self.task
|
||||
lock.unlock()
|
||||
task?.resume()
|
||||
}
|
||||
|
||||
private func handleResponse(data: Data?, response: URLResponse?, error: Error?) {
|
||||
lock.lock()
|
||||
guard demand > 0, parent != nil, let downstream = self.downstream else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
lockedTerminate()
|
||||
lock.unlock()
|
||||
switch (data, response, error) {
|
||||
case let (data, response?, nil):
|
||||
_ = downstream.receive((data ?? Data(), response))
|
||||
downstream.receive(completion: .finished)
|
||||
case let (_, _, error as URLError):
|
||||
downstream.receive(completion: .failure(error))
|
||||
default:
|
||||
downstream.receive(completion: .failure(URLError(.unknown)))
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
lock.lock()
|
||||
guard parent != nil else {
|
||||
lock.unlock()
|
||||
return
|
||||
}
|
||||
let task = self.task
|
||||
lockedTerminate()
|
||||
lock.unlock()
|
||||
task?.cancel()
|
||||
}
|
||||
|
||||
private func lockedTerminate() {
|
||||
parent = nil
|
||||
downstream = nil
|
||||
demand = .none
|
||||
task = nil
|
||||
}
|
||||
|
||||
var description: String { return "DataTaskPublisher" }
|
||||
|
||||
var customMirror: Mirror {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
let children: [Mirror.Child] = [
|
||||
("task", task as Any),
|
||||
("downstream", downstream as Any),
|
||||
("parent", parent as Any),
|
||||
("demand", demand)
|
||||
]
|
||||
return Mirror(self, children: children)
|
||||
}
|
||||
|
||||
var playgroundDescription: Any { return description }
|
||||
}
|
||||
}
|
||||
@@ -23,34 +23,13 @@ final class DispatchQueueSchedulerTests: XCTestCase {
|
||||
func testSchedulerTimeTypeDistance() {
|
||||
let time1 = Scheduler.SchedulerTimeType(.init(uptimeNanoseconds: 10000))
|
||||
let time2 = Scheduler.SchedulerTimeType(.init(uptimeNanoseconds: 10431))
|
||||
let distantFuture = Scheduler.SchedulerTimeType(.distantFuture)
|
||||
let notSoDistantFuture = Scheduler.SchedulerTimeType(
|
||||
DispatchTime(
|
||||
uptimeNanoseconds: DispatchTime.distantFuture.uptimeNanoseconds - 1024
|
||||
)
|
||||
)
|
||||
|
||||
XCTAssertEqual(time1.distance(to: time2), .nanoseconds(431))
|
||||
XCTAssertEqual(time2.distance(to: time1), .nanoseconds(-431))
|
||||
|
||||
XCTAssertEqual(time1.distance(to: distantFuture), .nanoseconds(-10001))
|
||||
XCTAssertEqual(distantFuture.distance(to: time1), .nanoseconds(10001))
|
||||
XCTAssertEqual(time2.distance(to: distantFuture), .nanoseconds(-10432))
|
||||
XCTAssertEqual(distantFuture.distance(to: time2), .nanoseconds(10432))
|
||||
|
||||
XCTAssertEqual(time1.distance(to: notSoDistantFuture), .nanoseconds(-11025))
|
||||
XCTAssertEqual(notSoDistantFuture.distance(to: time1), .nanoseconds(11025))
|
||||
XCTAssertEqual(time2.distance(to: notSoDistantFuture), .nanoseconds(-11456))
|
||||
XCTAssertEqual(notSoDistantFuture.distance(to: time2), .nanoseconds(11456))
|
||||
|
||||
XCTAssertEqual(distantFuture.distance(to: distantFuture), .nanoseconds(0))
|
||||
XCTAssertEqual(notSoDistantFuture.distance(to: notSoDistantFuture),
|
||||
.nanoseconds(0))
|
||||
}
|
||||
|
||||
func testSchedulerTimeTypeAdvanced() {
|
||||
let time = Scheduler.SchedulerTimeType(.init(uptimeNanoseconds: 10000))
|
||||
let beginningOfTime = Scheduler.SchedulerTimeType(.init(uptimeNanoseconds: 1))
|
||||
let stride1 = Scheduler.SchedulerTimeType.Stride.nanoseconds(431)
|
||||
let stride2 = Scheduler.SchedulerTimeType.Stride.nanoseconds(-220)
|
||||
|
||||
@@ -59,12 +38,6 @@ final class DispatchQueueSchedulerTests: XCTestCase {
|
||||
|
||||
XCTAssertEqual(time.advanced(by: stride2),
|
||||
Scheduler.SchedulerTimeType(.init(uptimeNanoseconds: 9780)))
|
||||
|
||||
XCTAssertEqual(time.advanced(by: .nanoseconds(.max)).dispatchTime,
|
||||
.distantFuture)
|
||||
|
||||
XCTAssertEqual(beginningOfTime.advanced(by: .nanoseconds(-1000)).dispatchTime,
|
||||
DispatchTime(uptimeNanoseconds: 1))
|
||||
}
|
||||
|
||||
func testSchedulerTimeTypeEquatable() {
|
||||
@@ -111,145 +84,41 @@ final class DispatchQueueSchedulerTests: XCTestCase {
|
||||
// MARK: - Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
func testStrideToDispatchTimeInterval() {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
switch (Stride.seconds(2).timeInterval,
|
||||
Stride.milliseconds(2).timeInterval,
|
||||
Stride.microseconds(2).timeInterval,
|
||||
Stride.nanoseconds(2).timeInterval,
|
||||
Stride.nanoseconds(.max).timeInterval) {
|
||||
Stride.nanoseconds(2).timeInterval) {
|
||||
case (.nanoseconds(2_000_000_000),
|
||||
.nanoseconds(2_000_000),
|
||||
.nanoseconds(2_000),
|
||||
.nanoseconds(2),
|
||||
.nanoseconds(.max)):
|
||||
.nanoseconds(2)):
|
||||
break // pass
|
||||
case let intervals:
|
||||
XCTFail("Unexpected DispatchTimeInterval: \(intervals)")
|
||||
}
|
||||
}
|
||||
|
||||
func testStrideFromDispatchTimeInterval() throws {
|
||||
func testStrideFromDispatchTimeInterval() {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
XCTAssertEqual(Stride(.seconds(2)).magnitude, 2_000_000_000)
|
||||
XCTAssertEqual(Stride(.milliseconds(2)).magnitude, 2_000_000)
|
||||
XCTAssertEqual(Stride(.microseconds(2)).magnitude, 2_000)
|
||||
XCTAssertEqual(Stride(.nanoseconds(2)).magnitude, 2)
|
||||
|
||||
XCTAssertEqual(Stride(.never).magnitude, .max)
|
||||
XCTAssertEqual(Stride(.nanoseconds(.max)).magnitude, .max)
|
||||
XCTAssertEqual(Stride(.nanoseconds(.min)).magnitude, .min)
|
||||
XCTAssertEqual(Stride(.microseconds(.max)).magnitude, .max)
|
||||
XCTAssertEqual(Stride(.microseconds(.min)).magnitude, .min)
|
||||
XCTAssertEqual(Stride(.milliseconds(.max)).magnitude, .max)
|
||||
XCTAssertEqual(Stride(.milliseconds(.min)).magnitude, .min)
|
||||
XCTAssertEqual(Stride(.seconds(.max)).magnitude, .max)
|
||||
XCTAssertEqual(Stride(.seconds(.min)).magnitude, .min)
|
||||
}
|
||||
|
||||
func testStrideFromUnknownDispatchTimeIntervalCase() {
|
||||
// Here we're testing out internal API that is not present in Combine.
|
||||
// Although we prefer only testing public APIs, this case is special.
|
||||
let makeStride: (DispatchTimeInterval) -> Stride
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST
|
||||
makeStride = Stride.init(_:)
|
||||
#else
|
||||
makeStride = Stride.init(__guessFromUnknown:)
|
||||
#endif
|
||||
|
||||
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
|
||||
// 64-bit platforms
|
||||
let minNanoseconds = -0x13B13B13B13B13B0 // Int64.min / 6.5
|
||||
let maxNanoseconds = 0x2C4EC4EC4EC4EC4D // Int64.max / 2.889
|
||||
#elseif arch(i386) || arch(arm)
|
||||
// 32-bit platforms
|
||||
let minNanoseconds = Int.min + 1
|
||||
let maxNanoseconds = Int.max
|
||||
#else
|
||||
#error("This architecture isn't known. Add it to the 32-bit or 64-bit line.")
|
||||
#endif
|
||||
XCTAssertEqual(makeStride(.nanoseconds(minNanoseconds)).magnitude, minNanoseconds)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(-128)).magnitude, -128)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(-57)).magnitude, -57)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(-33)).magnitude, -33)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(-17)).magnitude, -17)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(-8)).magnitude, -8)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(-3)).magnitude, -3)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(-1)).magnitude, -1)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(0)).magnitude, 0)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(1)).magnitude, 1)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(3)).magnitude, 3)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(8)).magnitude, 8)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(17)).magnitude, 17)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(33)).magnitude, 33)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(57)).magnitude, 57)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(128)).magnitude, 128)
|
||||
XCTAssertEqual(makeStride(.nanoseconds(maxNanoseconds)).magnitude, maxNanoseconds)
|
||||
|
||||
XCTAssertEqual(makeStride(.microseconds(-128)).magnitude, -128_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(-57)).magnitude, -57_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(-33)).magnitude, -33_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(-17)).magnitude, -17_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(-8)).magnitude, -8_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(-3)).magnitude, -3_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(-1)).magnitude, -1_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(0)).magnitude, 0)
|
||||
XCTAssertEqual(makeStride(.microseconds(1)).magnitude, 1_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(3)).magnitude, 3_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(8)).magnitude, 8_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(17)).magnitude, 17_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(33)).magnitude, 33_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(57)).magnitude, 57_000)
|
||||
XCTAssertEqual(makeStride(.microseconds(128)).magnitude, 128_000)
|
||||
|
||||
XCTAssertEqual(makeStride(.milliseconds(-128)).magnitude, -128_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(-57)).magnitude, -57_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(-33)).magnitude, -33_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(-17)).magnitude, -17_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(-8)).magnitude, -8_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(-3)).magnitude, -3_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(-1)).magnitude, -1_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(0)).magnitude, 0)
|
||||
XCTAssertEqual(makeStride(.milliseconds(1)).magnitude, 1_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(3)).magnitude, 3_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(8)).magnitude, 8_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(17)).magnitude, 17_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(33)).magnitude, 33_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(57)).magnitude, 57_000_000)
|
||||
XCTAssertEqual(makeStride(.milliseconds(128)).magnitude, 128_000_000)
|
||||
|
||||
XCTAssertEqual(makeStride(.seconds(-2)).magnitude, -2_000_000_000)
|
||||
XCTAssertEqual(makeStride(.seconds(-1)).magnitude, -1_000_000_000)
|
||||
XCTAssertEqual(makeStride(.seconds(0)).magnitude, 0)
|
||||
XCTAssertEqual(makeStride(.seconds(1)).magnitude, 1_000_000_000)
|
||||
XCTAssertEqual(makeStride(.seconds(2)).magnitude, 2_000_000_000)
|
||||
}
|
||||
|
||||
func testStrideFromNumericValue() {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
XCTAssertEqual(Stride.seconds(1.2).magnitude, 1_200_000_000)
|
||||
XCTAssertEqual(Stride.seconds(2).magnitude, 2_000_000_000)
|
||||
XCTAssertEqual(Stride.milliseconds(2).magnitude, 2_000_000)
|
||||
XCTAssertEqual(Stride.microseconds(2).magnitude, 2_000)
|
||||
XCTAssertEqual(Stride.nanoseconds(2).magnitude, 2)
|
||||
|
||||
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
|
||||
// 64-bit platforms
|
||||
XCTAssertEqual(
|
||||
Stride.seconds(Double(Int.max) / 1_000_000_000 - 1).magnitude,
|
||||
9223372035854776320
|
||||
)
|
||||
#elseif arch(i386) || arch(arm)
|
||||
// 32-bit platforms
|
||||
XCTAssertEqual(
|
||||
Stride.seconds(Double(Int.max) / 1_000_000_000).magnitude,
|
||||
.max
|
||||
)
|
||||
#else
|
||||
#error("This architecture isn't known. Add it to the 32-bit or 64-bit line.")
|
||||
#endif
|
||||
|
||||
XCTAssertEqual(Stride.seconds(.max).magnitude, .max)
|
||||
XCTAssertEqual(Stride.milliseconds(.max).magnitude, .max)
|
||||
XCTAssertEqual(Stride.microseconds(.max).magnitude, .max)
|
||||
XCTAssertEqual(Stride.nanoseconds(.max).magnitude, .max)
|
||||
|
||||
XCTAssertEqual((1.2 as Stride).magnitude, 1_200_000_000)
|
||||
XCTAssertEqual((2 as Stride).magnitude, 2_000_000_000)
|
||||
|
||||
@@ -257,33 +126,17 @@ final class DispatchQueueSchedulerTests: XCTestCase {
|
||||
XCTAssertEqual(Stride(exactly: 871 as UInt64)?.magnitude, 871)
|
||||
}
|
||||
|
||||
func testStrideFromTooMuchSecondsCrashes() {
|
||||
assertCrashes {
|
||||
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
|
||||
// 64-bit platforms
|
||||
XCTAssertGreaterThan(
|
||||
Stride.seconds(Double(Int.max) / 1_000_000_000).magnitude,
|
||||
.max
|
||||
)
|
||||
#elseif arch(i386) || arch(arm)
|
||||
// 32-bit platforms
|
||||
XCTAssertGreaterThan(
|
||||
Stride.seconds(Double(Int.max) / 1_000_000_000 + 1).magnitude,
|
||||
.max
|
||||
)
|
||||
#else
|
||||
#error("This architecture isn't known. Add it to the 32-bit or 64-bit line.")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
func testStrideComparable() {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
XCTAssertLessThan(Stride.nanoseconds(1), .nanoseconds(2))
|
||||
XCTAssertGreaterThan(Stride.nanoseconds(-2), .microseconds(-10))
|
||||
XCTAssertLessThan(Stride.milliseconds(2), .seconds(2))
|
||||
}
|
||||
|
||||
func testStrideMultiplication() {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
XCTAssertEqual((Stride.nanoseconds(0) * .nanoseconds(61346)).magnitude, 0)
|
||||
XCTAssertEqual((Stride.nanoseconds(61346) * .nanoseconds(0)).magnitude, 0)
|
||||
XCTAssertEqual((Stride.nanoseconds(18) * .nanoseconds(1)).magnitude, 18)
|
||||
@@ -343,6 +196,8 @@ final class DispatchQueueSchedulerTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testStrideAddition() {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
XCTAssertEqual((Stride.nanoseconds(0) + .microseconds(2)).magnitude, 2000)
|
||||
XCTAssertEqual((Stride.nanoseconds(2) + .microseconds(0)).magnitude, 2)
|
||||
XCTAssertEqual((Stride.nanoseconds(7) + .nanoseconds(12)).magnitude, 19)
|
||||
@@ -388,6 +243,8 @@ final class DispatchQueueSchedulerTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testStrideSubtraction() {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
XCTAssertEqual((Stride.nanoseconds(0) - .microseconds(2)).magnitude, -2000)
|
||||
XCTAssertEqual((Stride.nanoseconds(2) - .microseconds(0)).magnitude, 2)
|
||||
XCTAssertEqual((Stride.nanoseconds(7) - .nanoseconds(12)).magnitude, -5)
|
||||
@@ -433,6 +290,8 @@ final class DispatchQueueSchedulerTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testStrideCodable() throws {
|
||||
typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
@@ -570,9 +429,6 @@ private let backgroundScheduler = DispatchQueue.global(qos: .background).ocombin
|
||||
|
||||
#endif
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
private typealias Stride = Scheduler.SchedulerTimeType.Stride
|
||||
|
||||
private struct KeyedWrapper<Value: Codable & Equatable>: Codable, Equatable {
|
||||
let value: Value
|
||||
}
|
||||
|
||||
@@ -0,0 +1,482 @@
|
||||
//
|
||||
// EnumerateFieldsTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 29.11.2019.
|
||||
//
|
||||
|
||||
import CoreFoundation
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
// This file contains tests for internal OpenCombine APIs.
|
||||
|
||||
#if !OPENCOMBINE_COMPATIBILITY_TEST
|
||||
|
||||
final class EnumerateFieldsTests: TestCase {
|
||||
|
||||
func testClassNoFields() {
|
||||
enumerateFields(ofType: NoFields.self, allowResilientSuperclasses: true) { _ in
|
||||
XCTFail("should not be called")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func testClassVarsAndLets() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: VarsAndLets.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
if field.name == "stopEnumerating" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("constant1", 16, Int.self),
|
||||
.init("constant2", 0, Void.self),
|
||||
.init("variable1", 24, String.self),
|
||||
.init("variable2", 40, Double.self),
|
||||
.init("stopEnumerating", 48, Int.self)])
|
||||
if hasFailed { return }
|
||||
let instance = VarsAndLets()
|
||||
XCTAssertEqual(loadField(fields[0], from: instance, as: Int.self), 42)
|
||||
loadField(fields[1], from: instance, as: Void.self)
|
||||
XCTAssertEqual(loadField(fields[2], from: instance, as: String.self), "hello")
|
||||
XCTAssertEqual(loadField(fields[3], from: instance, as: Double.self), 12.3)
|
||||
XCTAssertEqual(loadField(fields[4], from: instance, as: Int.self), -1)
|
||||
}
|
||||
|
||||
func testRegularDerivedClass() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: RegularDerived.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("field1", 16, Int.self),
|
||||
.init("field2", 24, Bool.self),
|
||||
.init("field3", 25, Bool.self),
|
||||
.init("field4", 32, String.self),
|
||||
.init("field5", 48, Int.self)])
|
||||
if hasFailed { return }
|
||||
let instance = RegularDerived()
|
||||
XCTAssertEqual(loadField(fields[0], from: instance, as: Int.self), 1)
|
||||
XCTAssertEqual(loadField(fields[1], from: instance, as: Bool.self), false)
|
||||
XCTAssertEqual(loadField(fields[2], from: instance, as: Bool.self), true)
|
||||
XCTAssertEqual(loadField(fields[3], from: instance, as: String.self), "3")
|
||||
XCTAssertEqual(loadField(fields[4], from: instance, as: Int.self), 4)
|
||||
}
|
||||
|
||||
func testRegularDerivedClassEarlyExit() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: RegularDerived.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
if field.name == "field2" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("field1", 16, Int.self),
|
||||
.init("field2", 24, Bool.self)])
|
||||
}
|
||||
|
||||
func testObjCClass() {
|
||||
// All Foundation classes are native Swift classes on non-Darwin platforms
|
||||
#if canImport(Darwin)
|
||||
enumerateFields(ofType: NSNumber.self,
|
||||
allowResilientSuperclasses: true) { _ in
|
||||
XCTFail("should not be called")
|
||||
return false
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func testSwiftSubclassOfObjCClass() {
|
||||
// All Foundation classes are native Swift classes on non-Darwin platforms
|
||||
#if canImport(Darwin)
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: ObjCDerived.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
if field.name == "field2" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("field1", 8, Int.self),
|
||||
.init("field2", 16, Bool.self)])
|
||||
if hasFailed { return }
|
||||
let instance = ObjCDerived()
|
||||
XCTAssertEqual(loadField(fields[0], from: instance, as: Int.self), 1)
|
||||
XCTAssertEqual(loadField(fields[1], from: instance, as: Bool.self), true)
|
||||
#endif
|
||||
}
|
||||
|
||||
func testNSObjectSubclass() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: DerivedFromNSObject.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
#if canImport(Darwin)
|
||||
XCTAssertEqual(fields, [.init("field1", 8, Int.self),
|
||||
.init("field2", 16, Bool.self),
|
||||
.init("field3", 17, Bool.self)])
|
||||
#else
|
||||
XCTAssertEqual(fields, [.init("field1", 16, Int.self),
|
||||
.init("field2", 24, Bool.self),
|
||||
.init("field3", 25, Bool.self)])
|
||||
#endif
|
||||
if hasFailed { return }
|
||||
let instance = DerivedFromNSObject()
|
||||
XCTAssertEqual(loadField(fields[0], from: instance, as: Int.self), 1)
|
||||
XCTAssertEqual(loadField(fields[1], from: instance, as: Bool.self), true)
|
||||
XCTAssertEqual(loadField(fields[2], from: instance, as: Bool.self), false)
|
||||
}
|
||||
|
||||
func testResilientClass() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: JSONDecoder.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertFalse(fields.isEmpty)
|
||||
}
|
||||
|
||||
func testSubclassOfResilientClass() {
|
||||
#if canImport(Darwin) // There are no resilient classes on non-Darwin platforms
|
||||
enumerateFields(ofType: DerivedFromResilientClass.self,
|
||||
allowResilientSuperclasses: false) { _ in
|
||||
XCTFail("should not be called")
|
||||
return true
|
||||
}
|
||||
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: DerivedFromResilientClass.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertFalse(fields.isEmpty)
|
||||
#endif
|
||||
}
|
||||
|
||||
func testGenericClass() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: GenericBase<String, Decimal>.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("field1", 16, String.self),
|
||||
.init("field2", 32, Decimal.self)])
|
||||
if hasFailed { return }
|
||||
let instance = GenericBase<String, Decimal>("foo", 13.5)
|
||||
XCTAssertEqual(loadField(fields[0], from: instance, as: String.self), "foo")
|
||||
XCTAssertEqual(loadField(fields[1], from: instance, as: Decimal.self), 13.5)
|
||||
}
|
||||
|
||||
func testGenericSubclassOfGenericClass() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: GenericDerived<String, Int, Bool, [Int]>.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("field1", 16, String.self),
|
||||
.init("field2", 32, Int.self),
|
||||
.init("field3", 40, Bool.self),
|
||||
.init("field4", 48, [Int].self)])
|
||||
if hasFailed { return }
|
||||
let instance = GenericDerived("foo", 42, true, [1, 2, 3])
|
||||
XCTAssertEqual(loadField(fields[0], from: instance, as: String.self), "foo")
|
||||
XCTAssertEqual(loadField(fields[1], from: instance, as: Int.self), 42)
|
||||
XCTAssertEqual(loadField(fields[2], from: instance, as: Bool.self), true)
|
||||
XCTAssertEqual(loadField(fields[3], from: instance, as: [Int].self), [1, 2, 3])
|
||||
}
|
||||
|
||||
func testGenericSubclassOfNonGenericResilientClass() {
|
||||
#if canImport(Darwin) // There are no resilient classes on non-Darwin platforms
|
||||
enumerateFields(ofType: GenericDerivedFromResilientBase<Int, Int>.self,
|
||||
allowResilientSuperclasses: false) { _ in
|
||||
XCTFail("should not be called")
|
||||
return true
|
||||
}
|
||||
|
||||
var superclassFields = [FieldInfo]()
|
||||
enumerateFields(ofType: JSONDecoder.self,
|
||||
allowResilientSuperclasses: false) { field in
|
||||
superclassFields.append(field)
|
||||
return true
|
||||
}
|
||||
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: GenericDerivedFromResilientBase<Int, Int>.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, superclassFields + [.init("field1", 128, Int.self),
|
||||
.init("field2", 136, Int.self)])
|
||||
#endif
|
||||
}
|
||||
|
||||
func testForeignClass() {
|
||||
enumerateFields(ofType: CFMutableArray.self,
|
||||
allowResilientSuperclasses: true) { _ in
|
||||
XCTFail("should not be called")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func testClassWithFieldsOfResilientTypes() {
|
||||
#if canImport(Darwin)
|
||||
guard #available(macOS 10.12, iOS 10.0, *) else { return }
|
||||
#endif
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: HasResilientFields.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("field1", 16, IndexPath.self),
|
||||
.init("field2", 40, Measurement<UnitSpeed>.self),
|
||||
.init("field3", 56, Bool.self)])
|
||||
if hasFailed { return }
|
||||
let instance = HasResilientFields()
|
||||
XCTAssertEqual(loadField(fields[0], from: instance, as: IndexPath.self), [42, 12])
|
||||
XCTAssertEqual(loadField(fields[1],
|
||||
from: instance,
|
||||
as: Measurement<UnitSpeed>.self),
|
||||
Measurement<UnitSpeed>(value: 12, unit: .metersPerSecond))
|
||||
XCTAssertEqual(loadField(fields[2], from: instance, as: Bool.self), true)
|
||||
}
|
||||
|
||||
func testStructLetsAndVars() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: CommonValue.self,
|
||||
allowResilientSuperclasses: false) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
|
||||
XCTAssertEqual(fields, [.init("field1", 0, Int.self),
|
||||
.init("field2", 8, Bool.self),
|
||||
.init("field3", 9, Bool.self),
|
||||
.init("field4", 16, [String].self),
|
||||
.init("field5", 0, Void.self)])
|
||||
if hasFailed { return }
|
||||
let value = CommonValue(field1: 42,
|
||||
field2: true,
|
||||
field3: false,
|
||||
field4: ["it", "works"],
|
||||
field5: ())
|
||||
XCTAssertEqual(loadField(fields[0], from: value, as: Int.self), 42)
|
||||
XCTAssertEqual(loadField(fields[1], from: value, as: Bool.self), true)
|
||||
XCTAssertEqual(loadField(fields[2], from: value, as: Bool.self), false)
|
||||
XCTAssertEqual(loadField(fields[3], from: value, as: [String].self),
|
||||
["it", "works"])
|
||||
loadField(fields[4], from: value, as: Void.self)
|
||||
}
|
||||
|
||||
func testGenericStruct() {
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: GenericValue<Int, String>.self,
|
||||
allowResilientSuperclasses: false) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("field1", 0, Int.self),
|
||||
.init("field2", 8, String.self),
|
||||
.init("field3", 24, Bool.self)])
|
||||
if hasFailed { return }
|
||||
let value = GenericValue(field1: 12345678, field2: "🦊", field3: true)
|
||||
XCTAssertEqual(loadField(fields[0], from: value, as: Int.self), 12345678)
|
||||
XCTAssertEqual(loadField(fields[1], from: value, as: String.self), "🦊")
|
||||
XCTAssertEqual(loadField(fields[2], from: value, as: Bool.self), true)
|
||||
}
|
||||
|
||||
func testResilientStruct() {
|
||||
#if canImport(Darwin) // There are no resilient classes on non-Darwin platforms
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: Notification.self,
|
||||
allowResilientSuperclasses: false) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("name", 0, Notification.Name.self),
|
||||
.init("object", 8, Any?.self),
|
||||
.init("userInfo", 40, [AnyHashable : Any]?.self)])
|
||||
if hasFailed { return }
|
||||
let value = Notification(name: .init("some note"),
|
||||
object: ["a", "b"] as Set<String>,
|
||||
userInfo: ["a" : 1, "b": 2])
|
||||
XCTAssertEqual(loadField(fields[0], from: value, as: Notification.Name.self),
|
||||
.init("some note"))
|
||||
|
||||
XCTAssertEqual(loadField(fields[1], from: value, as: Any?.self) as? Set<String>,
|
||||
["a", "b"])
|
||||
#endif
|
||||
}
|
||||
|
||||
func testTuple() {
|
||||
enumerateFields(ofType: Void.self, allowResilientSuperclasses: false) { _ in
|
||||
XCTFail("should not be called")
|
||||
return true
|
||||
}
|
||||
|
||||
typealias Tuple =
|
||||
(Int, String, label1: Double, Bool, s̈pin̈al_tap̈: IndexPath, label3: Float)
|
||||
var fields = [FieldInfo]()
|
||||
enumerateFields(ofType: Tuple.self,
|
||||
allowResilientSuperclasses: true) { field in
|
||||
fields.append(field)
|
||||
return true
|
||||
}
|
||||
XCTAssertEqual(fields, [.init("", 0, Int.self),
|
||||
.init("", 8, String.self),
|
||||
.init("label1", 24, Double.self),
|
||||
.init("", 32, Bool.self),
|
||||
.init("s̈pin̈al_tap̈", 40, IndexPath.self),
|
||||
.init("label3", 60, Float.self)])
|
||||
if hasFailed { return }
|
||||
let value: Tuple = (1234, "🌚", 59.1, false, [9, 3, 1], 10.1)
|
||||
XCTAssertEqual(loadField(fields[0], from: value, as: Int.self), 1234)
|
||||
XCTAssertEqual(loadField(fields[1], from: value, as: String.self), "🌚")
|
||||
XCTAssertEqual(loadField(fields[2], from: value, as: Double.self), 59.1)
|
||||
XCTAssertEqual(loadField(fields[3], from: value, as: Bool.self), false)
|
||||
XCTAssertEqual(loadField(fields[4], from: value, as: IndexPath.self), [9, 3, 1])
|
||||
XCTAssertEqual(loadField(fields[5], from: value, as: Float.self), 10.1)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadField<FieldType>(_ field: FieldInfo,
|
||||
from instance: AnyObject,
|
||||
as type: FieldType.Type,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line) -> FieldType? {
|
||||
if field.type != type {
|
||||
XCTFail("Type mismatch", file: file, line: line)
|
||||
return nil
|
||||
}
|
||||
return Unmanaged
|
||||
.passUnretained(instance)
|
||||
.toOpaque()
|
||||
.load(fromByteOffset: field.offset, as: type)
|
||||
}
|
||||
|
||||
private func loadField<Value, FieldType>(_ field: FieldInfo,
|
||||
from value: Value,
|
||||
as type: FieldType.Type,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line) -> FieldType? {
|
||||
if field.type != type {
|
||||
XCTFail("Type mismatch", file: file, line: line)
|
||||
return nil
|
||||
}
|
||||
return withUnsafePointer(to: value) {
|
||||
UnsafeRawPointer($0).load(fromByteOffset: field.offset, as: type)
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable generic_type_name
|
||||
|
||||
private final class NoFields {}
|
||||
|
||||
private final class VarsAndLets {
|
||||
let constant1 = 42
|
||||
let constant2: Void = ()
|
||||
var variable1 = "hello"
|
||||
var variable2 = 12.3
|
||||
let stopEnumerating = -1
|
||||
var neverVisited = 10
|
||||
}
|
||||
|
||||
private class RegularBase {
|
||||
var field1 = 1
|
||||
var field2 = false
|
||||
var field3 = true
|
||||
}
|
||||
|
||||
private final class RegularDerived: RegularBase {
|
||||
var field4 = "3"
|
||||
var field5 = 4
|
||||
}
|
||||
|
||||
private final class ObjCDerived: NSOrderedSet {
|
||||
var field1 = 1
|
||||
var field2 = true
|
||||
let field3 = false
|
||||
}
|
||||
|
||||
private final class DerivedFromNSObject: NSObject {
|
||||
var field1 = 1
|
||||
var field2 = true
|
||||
let field3 = false
|
||||
}
|
||||
|
||||
private final class DerivedFromResilientClass: JSONDecoder {
|
||||
var field1 = 1
|
||||
var field2 = "hello"
|
||||
}
|
||||
|
||||
private class GenericBase<A, B> {
|
||||
var field1: A
|
||||
var field2: B
|
||||
|
||||
init(_ field1: A, _ field2: B) {
|
||||
self.field1 = field1
|
||||
self.field2 = field2
|
||||
}
|
||||
}
|
||||
|
||||
private final class GenericDerived<A, B, C, D>: GenericBase<A, B> {
|
||||
var field3: C
|
||||
var field4: D
|
||||
|
||||
init(_ field1: A, _ field2: B, _ field3: C, _ field4: D) {
|
||||
self.field3 = field3
|
||||
self.field4 = field4
|
||||
super.init(field1, field2)
|
||||
}
|
||||
}
|
||||
|
||||
private class GenericDerivedFromResilientBase<A, B>: JSONDecoder {
|
||||
var field1: A
|
||||
var field2: B
|
||||
|
||||
init(_ field1: A, _ field2: B) {
|
||||
self.field1 = field1
|
||||
self.field2 = field2
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.12, iOS 10.0, *)
|
||||
private final class HasResilientFields {
|
||||
// Foundation.IndexPath is resilient struct
|
||||
var field1 = IndexPath(indexes: [42, 12])
|
||||
|
||||
// Foundation.Measurement is resilient generic struct
|
||||
let field2 = Measurement<UnitSpeed>(value: 12, unit: .metersPerSecond)
|
||||
|
||||
var field3 = true
|
||||
}
|
||||
|
||||
private struct CommonValue {
|
||||
var field1: Int
|
||||
let field2: Bool
|
||||
let field3: Bool
|
||||
var field4: [String]
|
||||
let field5: ()
|
||||
}
|
||||
|
||||
private struct GenericValue<A, B> {
|
||||
let field1: A
|
||||
let field2: B
|
||||
let field3: Bool
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TopLevelDecoder+Extensions.swift
|
||||
//
|
||||
//
|
||||
// Created by Joseph Spadafora on 6/29/19.
|
||||
//
|
||||
|
||||
#if !OPENCOMBINE_COMPATIBILITY_TEST
|
||||
import Foundation
|
||||
import OpenCombine
|
||||
|
||||
extension JSONDecoder: TopLevelDecoder {}
|
||||
extension JSONEncoder: TopLevelEncoder {}
|
||||
|
||||
extension PropertyListDecoder: TopLevelDecoder {}
|
||||
extension PropertyListEncoder: TopLevelEncoder {}
|
||||
#endif
|
||||
@@ -1,63 +0,0 @@
|
||||
//
|
||||
// JSONDecoderTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST
|
||||
import Combine
|
||||
#else
|
||||
import OpenCombine
|
||||
import OpenCombineFoundation
|
||||
#endif
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
final class JSONDecoderTests: XCTestCase {
|
||||
func testSuccessfullyDecode() {
|
||||
let decoder = JSONDecoder()
|
||||
let input = #"[{"success":true}]"#
|
||||
var actualOutput: [Subscribers.Completion<TestingError>]?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
let cancellable = Just(input)
|
||||
.map { Data($0.utf8) }
|
||||
.decode(type: [Subscribers.Completion<TestingError>].self, decoder: decoder)
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTAssertEqual(actualOutput, [.finished])
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
func testDecodingFailure() {
|
||||
let decoder = JSONDecoder()
|
||||
let input = #"{"a":1,"b":2}"#
|
||||
var actualOutput: [Int]?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
let cancellable = Just(input)
|
||||
.map { Data($0.utf8) }
|
||||
.decode(type: [Int].self, decoder: decoder)
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTFail("Unexpected success")
|
||||
case .failure(DecodingError.typeMismatch)?:
|
||||
XCTAssertNil(actualOutput)
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
//
|
||||
// JSONEncoderTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST
|
||||
import Combine
|
||||
#else
|
||||
import OpenCombine
|
||||
import OpenCombineFoundation
|
||||
#endif
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
final class JSONEncoderTests: XCTestCase {
|
||||
|
||||
func testSuccessfullyEncode() {
|
||||
let encoder = JSONEncoder()
|
||||
let input = [Subscribers.Completion<TestingError>.finished]
|
||||
var actualOutput: String?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
|
||||
let cancellable = Just(input)
|
||||
.encode(encoder: encoder)
|
||||
.map { String(decoding: $0, as: UTF8.self) }
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTAssertEqual(actualOutput, #"[{"success":true}]"#)
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
func testEncodingFailure() {
|
||||
let encoder = JSONEncoder()
|
||||
let input = Double.nan
|
||||
var actualOutput: String?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
let cancellable = Just(input)
|
||||
.encode(encoder: encoder)
|
||||
.map { String(decoding: $0, as: UTF8.self) }
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTFail("Unexpected success")
|
||||
case .failure(EncodingError.invalidValue)?:
|
||||
XCTAssertNil(actualOutput)
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
}
|
||||
@@ -1,625 +0,0 @@
|
||||
//
|
||||
// NotificationCenterTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST
|
||||
import Combine
|
||||
#else
|
||||
import OpenCombine
|
||||
import OpenCombineFoundation
|
||||
#endif
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
final class NotificationCenterTests: XCTestCase {
|
||||
|
||||
func testRequestingDemand() {
|
||||
|
||||
let initialDemands: [Subscribers.Demand?] = [
|
||||
nil,
|
||||
.max(1),
|
||||
.max(2),
|
||||
.max(10),
|
||||
.unlimited
|
||||
]
|
||||
|
||||
let subsequentDemands: [[Subscribers.Demand]] = [
|
||||
Array(repeating: .max(0), count: 5),
|
||||
Array(repeating: .max(1), count: 10),
|
||||
[.max(1), .max(0), .max(1), .max(0)],
|
||||
[.max(0), .max(1), .max(2)],
|
||||
[.unlimited, .max(1)]
|
||||
]
|
||||
|
||||
var numberOfInputsHistory: [Int] = []
|
||||
let expectedNumberOfInputsHistory = [
|
||||
0, 0, 0, 0, 0, 1, 11, 2, 1, 20, 2, 12, 4, 5, 20, 10, 20, 12, 13, 20, 20,
|
||||
20, 20, 20, 20
|
||||
]
|
||||
|
||||
for initialDemand in initialDemands {
|
||||
for subsequentDemand in subsequentDemands {
|
||||
|
||||
var i = 0
|
||||
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let publisher = makePublisher(center, for: name, object: nil)
|
||||
|
||||
let subscriber = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { initialDemand.map($0.request) },
|
||||
receiveValue: { _ in
|
||||
defer { i += 1 }
|
||||
return i < subsequentDemand.endIndex ? subsequentDemand[i] : .none
|
||||
}
|
||||
)
|
||||
|
||||
XCTAssertEqual(subscriber.subscriptions.count, 0)
|
||||
XCTAssertEqual(subscriber.inputs.count, 0)
|
||||
XCTAssertEqual(subscriber.completions.count, 0)
|
||||
|
||||
publisher.subscribe(subscriber)
|
||||
|
||||
XCTAssertEqual(subscriber.subscriptions.count, 1)
|
||||
XCTAssertEqual(subscriber.inputs.count, 0)
|
||||
XCTAssertEqual(subscriber.completions.count, 0)
|
||||
|
||||
for _ in 0..<20 {
|
||||
center.post(name: name, object: TestObject.two)
|
||||
}
|
||||
|
||||
XCTAssertEqual(subscriber.subscriptions.count, 1)
|
||||
XCTAssertEqual(subscriber.completions.count, 0)
|
||||
|
||||
numberOfInputsHistory.append(subscriber.inputs.count)
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual(numberOfInputsHistory, expectedNumberOfInputsHistory)
|
||||
}
|
||||
|
||||
func testBasicBehavior() throws {
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let publisher = makePublisher(center, for: name, object: TestObject.one)
|
||||
var downstreamSubscription: Subscription?
|
||||
let tracking = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
|
||||
XCTAssertEqual(center.history, [])
|
||||
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, TestObject.one, nil)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer")])
|
||||
|
||||
let note = Notification(name: name, object: TestObject.one, userInfo: nil)
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, TestObject.one, nil),
|
||||
.postNotification(note)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer")])
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).request(.max(3))
|
||||
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, TestObject.one, nil),
|
||||
.postNotification(note),
|
||||
.postNotification(note)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer"),
|
||||
.value(note)])
|
||||
|
||||
let unrelatedNote1 = Notification(name: Notification.Name("unrelatedNote1"),
|
||||
object: TestObject.one,
|
||||
userInfo: nil)
|
||||
center.post(unrelatedNote1)
|
||||
|
||||
let unrelatedNote2 = Notification(name: name,
|
||||
object: TestObject.two,
|
||||
userInfo: nil)
|
||||
center.post(unrelatedNote2)
|
||||
center.post(name: name, object: nil)
|
||||
center.post(name: name, object: nil)
|
||||
center.post(name: name, object: TestObject.one)
|
||||
center.post(name: name, object: TestObject.one)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, TestObject.one, nil),
|
||||
.postNotification(note),
|
||||
.postNotification(note),
|
||||
.postNotification(unrelatedNote1),
|
||||
.postNotification(unrelatedNote2),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(Notification(name: name,
|
||||
object: TestObject.one)),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(Notification(name: name,
|
||||
object: TestObject.one))])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer"),
|
||||
.value(note),
|
||||
.value(unrelatedNote1),
|
||||
.value(unrelatedNote2)])
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).request(.unlimited)
|
||||
|
||||
center.post(note)
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, TestObject.one, nil),
|
||||
.postNotification(note),
|
||||
.postNotification(note),
|
||||
.postNotification(unrelatedNote1),
|
||||
.postNotification(unrelatedNote2),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(Notification(name: name,
|
||||
object: TestObject.one)),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(Notification(name: name,
|
||||
object: TestObject.one)),
|
||||
.postNotification(note),
|
||||
.removeObserver,
|
||||
.removeObserverForName(nil, nil),
|
||||
.postNotification(note)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer"),
|
||||
.value(note),
|
||||
.value(unrelatedNote1),
|
||||
.value(unrelatedNote2),
|
||||
.value(note)])
|
||||
}
|
||||
|
||||
func testBasicBehaviorNilObject() throws {
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let publisher = makePublisher(center, for: name, object: nil)
|
||||
var downstreamSubscription: Subscription?
|
||||
let tracking = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
|
||||
XCTAssertEqual(center.history, [])
|
||||
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer")])
|
||||
|
||||
let note = Notification(name: name, object: TestObject.one, userInfo: nil)
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil),
|
||||
.postNotification(note)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer")])
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).request(.max(3))
|
||||
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil),
|
||||
.postNotification(note),
|
||||
.postNotification(note)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer"),
|
||||
.value(note)])
|
||||
|
||||
let unrelatedNote = Notification(name: Notification.Name("unrelatedNote"),
|
||||
object: TestObject.one,
|
||||
userInfo: nil)
|
||||
center.post(unrelatedNote)
|
||||
center.post(name: name, object: nil)
|
||||
center.post(name: name, object: nil)
|
||||
center.post(name: name, object: TestObject.one)
|
||||
center.post(name: name, object: TestObject.one)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil),
|
||||
.postNotification(note),
|
||||
.postNotification(note),
|
||||
.postNotification(unrelatedNote),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(note),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(note)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer"),
|
||||
.value(note),
|
||||
.value(unrelatedNote),
|
||||
.value(Notification(name: name))])
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).request(.unlimited)
|
||||
|
||||
center.post(note)
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil),
|
||||
.postNotification(note),
|
||||
.postNotification(note),
|
||||
.postNotification(unrelatedNote),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name)),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(note),
|
||||
.postNotificationWithName(name, TestObject.one, nil),
|
||||
.postNotification(note),
|
||||
.postNotification(note),
|
||||
.removeObserver,
|
||||
.removeObserverForName(nil, nil),
|
||||
.postNotification(note)])
|
||||
XCTAssertEqual(tracking.history,
|
||||
[.subscription("NotificationCenter Observer"),
|
||||
.value(note),
|
||||
.value(unrelatedNote),
|
||||
.value(Notification(name: name)),
|
||||
.value(note)])
|
||||
}
|
||||
|
||||
func testRecursivelyReceiveValue() throws {
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let publisher = makePublisher(center, for: name, object: nil)
|
||||
let tracking = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { $0.request(.max(3)) },
|
||||
receiveValue: { _ in .unlimited }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
let note = Notification(name: name)
|
||||
var recursionCounter = 7
|
||||
tracking.onValue = { _ in
|
||||
if recursionCounter == 0 { return }
|
||||
recursionCounter -= 1
|
||||
center.post(note)
|
||||
}
|
||||
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(tracking.history, [.subscription("NotificationCenter Observer"),
|
||||
.value(note),
|
||||
.value(note),
|
||||
.value(note)])
|
||||
|
||||
center.post(note)
|
||||
|
||||
XCTAssertEqual(tracking.history, [.subscription("NotificationCenter Observer"),
|
||||
.value(note),
|
||||
.value(note),
|
||||
.value(note),
|
||||
.value(note),
|
||||
.value(note),
|
||||
.value(note),
|
||||
.value(note),
|
||||
.value(note)])
|
||||
}
|
||||
|
||||
func testCancelAlreadyCancelled() throws {
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let publisher = makePublisher(center, for: name, object: nil)
|
||||
var downstreamSubscription: Subscription?
|
||||
let tracking = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
|
||||
XCTAssertEqual(tracking.history, [.subscription("NotificationCenter Observer")])
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil),
|
||||
.removeObserver,
|
||||
.removeObserverForName(nil, nil)])
|
||||
}
|
||||
|
||||
func testCancellingReleasesNotificationCenter() throws {
|
||||
var centerDestroyed = false
|
||||
var downstreamSubscription: Subscription?
|
||||
do {
|
||||
let center = TestNotificationCenter()
|
||||
center.onDeinit = { centerDestroyed = true }
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let publisher = makePublisher(center, for: name, object: nil)
|
||||
let tracking = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
}
|
||||
XCTAssertFalse(centerDestroyed)
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
XCTAssertTrue(centerDestroyed)
|
||||
}
|
||||
|
||||
func testWeakCaptureWhenAddingObserver() {
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name("testName")
|
||||
var value: Notification?
|
||||
do {
|
||||
let publisher = makePublisher(center, for: name, object: nil)
|
||||
let tracking = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { $0.request(.max(1)) },
|
||||
receiveValue: { value = $0; return .none }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
tracking.clearHistory() // Release the subscription
|
||||
}
|
||||
center.post(name: name, object: nil)
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil),
|
||||
.postNotificationWithName(name, nil, nil),
|
||||
.postNotification(Notification(name: name))])
|
||||
XCTAssertNil(value)
|
||||
}
|
||||
|
||||
func testZeroDemand() throws {
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let publisher = makePublisher(center, for: name, object: nil)
|
||||
var downstreamSubscription: Subscription?
|
||||
let tracking = TrackingSubscriberBase<Notification, Never>(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
try XCTUnwrap(downstreamSubscription).request(.none)
|
||||
|
||||
XCTAssertEqual(center.history,
|
||||
[.addObserver(name, nil, nil)])
|
||||
XCTAssertEqual(tracking.history, [.subscription("NotificationCenter Observer")])
|
||||
}
|
||||
|
||||
func testNotificationCenterSubscriptionReflection() throws {
|
||||
let center = TestNotificationCenter()
|
||||
let name = Notification.Name(rawValue: "testName")
|
||||
let object = TestObject.one
|
||||
let publisher = makePublisher(center, for: name, object: object)
|
||||
|
||||
try testSubscriptionReflection(
|
||||
description: "NotificationCenter Observer",
|
||||
customMirror: expectedChildren(
|
||||
("center", .matches(String(describing: Optional(center)))),
|
||||
("name", .contains(String(describing: name))),
|
||||
("object", .matches(String(describing: Optional(object)))),
|
||||
("demand", "max(0)")
|
||||
),
|
||||
playgroundDescription: "NotificationCenter Observer",
|
||||
sut: publisher
|
||||
)
|
||||
}
|
||||
|
||||
func testEquatable() {
|
||||
let center1 = NotificationCenter()
|
||||
let center2 = NotificationCenter()
|
||||
let name1 = Notification.Name(rawValue: "abcdefg")
|
||||
let name2 = Notification.Name(rawValue: "1234567")
|
||||
let object1 = TestObject.one
|
||||
let object2 = TestObject.two
|
||||
|
||||
XCTAssertEqual(makePublisher(center1, for: name1, object: object1),
|
||||
makePublisher(center1, for: name1, object: object1))
|
||||
XCTAssertEqual(makePublisher(center2, for: name2, object: object2),
|
||||
makePublisher(center2, for: name2, object: object2))
|
||||
XCTAssertEqual(makePublisher(center1, for: name1, object: nil),
|
||||
makePublisher(center1, for: name1, object: nil))
|
||||
XCTAssertNotEqual(makePublisher(center1, for: name1, object: object1),
|
||||
makePublisher(center1, for: name1, object: nil))
|
||||
XCTAssertNotEqual(makePublisher(center1, for: name1, object: nil),
|
||||
makePublisher(center1, for: name1, object: object2))
|
||||
XCTAssertNotEqual(makePublisher(center1, for: name1, object: object1),
|
||||
makePublisher(center1, for: name1, object: object2))
|
||||
XCTAssertNotEqual(makePublisher(center1, for: name1, object: object1),
|
||||
makePublisher(center1, for: name2, object: object1))
|
||||
XCTAssertNotEqual(makePublisher(center1, for: name1, object: object1),
|
||||
makePublisher(center2, for: name1, object: object1))
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST || !canImport(Combine)
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
private func makePublisher(
|
||||
_ center: NotificationCenter,
|
||||
for name: Notification.Name,
|
||||
object: AnyObject?
|
||||
) -> NotificationCenter.Publisher {
|
||||
return center.publisher(for: name, object: object)
|
||||
}
|
||||
#else
|
||||
private func makePublisher(
|
||||
_ center: NotificationCenter,
|
||||
for name: Notification.Name,
|
||||
object: AnyObject?
|
||||
) -> NotificationCenter.OCombine.Publisher {
|
||||
return center.ocombine.publisher(for: name, object: object)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// A simple mock notification center that always sends notifications to **all**
|
||||
/// observers in non-thread safe manner.
|
||||
private final class TestNotificationCenter: NotificationCenter {
|
||||
|
||||
enum Event {
|
||||
case postNotificationWithName(Notification.Name, Any?, [AnyHashable : Any]?)
|
||||
case postNotification(Notification)
|
||||
case addObserver(Notification.Name?, Any?, OperationQueue?)
|
||||
case removeObserver
|
||||
case removeObserverForName(Notification.Name?, Any?)
|
||||
}
|
||||
|
||||
private final class Observation {
|
||||
let callback: (Notification) -> Void
|
||||
|
||||
init(callback: @escaping (Notification) -> Void) {
|
||||
self.callback = callback
|
||||
}
|
||||
}
|
||||
|
||||
private final class Token: NSObject {
|
||||
weak var observation: Observation?
|
||||
|
||||
init(observer: TestNotificationCenter.Observation) {
|
||||
self.observation = observer
|
||||
}
|
||||
}
|
||||
|
||||
private(set) var history = [Event]()
|
||||
|
||||
private var observations: [Observation] = []
|
||||
|
||||
var onDeinit: (() -> Void)?
|
||||
|
||||
deinit {
|
||||
onDeinit?()
|
||||
}
|
||||
|
||||
override func post(name aName: Notification.Name,
|
||||
object anObject: Any?,
|
||||
userInfo aUserInfo: [AnyHashable : Any]? = nil) {
|
||||
history.append(.postNotificationWithName(aName, anObject, aUserInfo))
|
||||
let notification = Notification(name: aName,
|
||||
object: anObject,
|
||||
userInfo: aUserInfo)
|
||||
post(notification)
|
||||
}
|
||||
|
||||
override func post(_ notification: Notification) {
|
||||
history.append(.postNotification(notification))
|
||||
for observation in observations {
|
||||
observation.callback(notification)
|
||||
}
|
||||
}
|
||||
|
||||
override func addObserver(
|
||||
forName name: NSNotification.Name?,
|
||||
object obj: Any?,
|
||||
queue: OperationQueue?,
|
||||
using block: @escaping (Notification) -> Void
|
||||
) -> NSObjectProtocol {
|
||||
history.append(.addObserver(name, obj, queue))
|
||||
let observer = Observation(callback: block)
|
||||
observations.append(observer)
|
||||
return Token(observer: observer)
|
||||
}
|
||||
|
||||
override func removeObserver(_ observer: Any) {
|
||||
history.append(.removeObserver)
|
||||
removeObserver(observer, name: nil, object: nil)
|
||||
}
|
||||
|
||||
override func removeObserver(_ observer: Any,
|
||||
name aName: NSNotification.Name?,
|
||||
object anObject: Any?) {
|
||||
history.append(.removeObserverForName(aName, anObject))
|
||||
guard let observer = observer as? Token else { return }
|
||||
observations.removeAll { $0 === observer.observation }
|
||||
}
|
||||
}
|
||||
|
||||
private final class TestObject: NSObject {
|
||||
|
||||
static let one = TestObject()
|
||||
|
||||
static let two = TestObject()
|
||||
}
|
||||
|
||||
extension TestNotificationCenter.Event: Equatable {
|
||||
fileprivate static func == (lhs: TestNotificationCenter.Event,
|
||||
rhs: TestNotificationCenter.Event) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case let (.postNotification(lhsNote), .postNotification(rhsNote)):
|
||||
return lhsNote == rhsNote
|
||||
case let (.postNotificationWithName(lhsName,
|
||||
lhsObject as TestObject?,
|
||||
lhsUserInfo),
|
||||
.postNotificationWithName(rhsName,
|
||||
rhsObject as TestObject?,
|
||||
rhsUserInfo)):
|
||||
return lhsName == rhsName &&
|
||||
lhsObject === rhsObject &&
|
||||
(lhsUserInfo == nil) == (rhsUserInfo == nil)
|
||||
case let (.addObserver(lhsName, lhsObject as TestObject?, lhsQueue),
|
||||
.addObserver(rhsName, rhsObject as TestObject?, rhsQueue)):
|
||||
return lhsName == rhsName &&
|
||||
lhsObject === rhsObject &&
|
||||
lhsQueue == rhsQueue
|
||||
case (.removeObserver, .removeObserver):
|
||||
return true
|
||||
case let (.removeObserverForName(lhsName, lhsObject as TestObject?),
|
||||
.removeObserverForName(rhsName, rhsObject as TestObject?)):
|
||||
return lhsName == rhsName && lhsObject === rhsObject
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TestNotificationCenter.Event: CustomStringConvertible {
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .postNotificationWithName(name, object, userInfo):
|
||||
return """
|
||||
.postNotificationWithName(\
|
||||
.init(rawValue: \"\(name.rawValue)\"), \
|
||||
\(object.map(String.init(describing:)) ?? "nil"), \
|
||||
\(userInfo.map(String.init(describing:)) ?? "nil"))
|
||||
"""
|
||||
case .postNotification:
|
||||
return ".postNotification(note)"
|
||||
case let .addObserver(name, object, queue):
|
||||
let nameDescription = name.map { ".init(rawValue: \($0.rawValue))" } ?? "nil"
|
||||
return """
|
||||
.addObserver(\
|
||||
\(nameDescription), \
|
||||
\(object.map(String.init(describing:)) ?? "nil"), \
|
||||
\(queue.map(String.init(describing:)) ?? "nil"))
|
||||
"""
|
||||
case .removeObserver:
|
||||
return ".removeObserver"
|
||||
case let .removeObserverForName(name, object):
|
||||
let nameDescription = name.map { ".init(rawValue: \($0.rawValue))" } ?? "nil"
|
||||
return """
|
||||
.removeObserverForName(\
|
||||
\(nameDescription), \
|
||||
\(object.map(String.init(describing:)) ?? "nil"))
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
//
|
||||
// PropertyListDecoderTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST
|
||||
import Combine
|
||||
#else
|
||||
import OpenCombine
|
||||
import OpenCombineFoundation
|
||||
#endif
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
final class PropertyListDecoderTests: XCTestCase {
|
||||
func testSuccessfullyDecode() {
|
||||
let decoder = PropertyListDecoder()
|
||||
let input = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" \
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
\t<dict>
|
||||
\t\t<key>success</key>
|
||||
\t\t<true/>
|
||||
\t</dict>
|
||||
</array>
|
||||
</plist>
|
||||
|
||||
"""
|
||||
var actualOutput: [Subscribers.Completion<TestingError>]?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
let cancellable = Just(input)
|
||||
.map { Data($0.utf8) }
|
||||
.decode(type: [Subscribers.Completion<TestingError>].self, decoder: decoder)
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTAssertEqual(actualOutput, [.finished])
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
func testDecodingFailure() {
|
||||
let decoder = PropertyListDecoder()
|
||||
let input = "000000"
|
||||
var actualOutput: [Int]?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
let cancellable = Just(input)
|
||||
.map { Data($0.utf8) }
|
||||
.decode(type: [Int].self, decoder: decoder)
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTFail("Unexpected success")
|
||||
case .failure(DecodingError.typeMismatch)?:
|
||||
XCTAssertNil(actualOutput)
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
//
|
||||
// PropertyListEncoderTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 10.12.2019.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST
|
||||
import Combine
|
||||
#else
|
||||
import OpenCombine
|
||||
import OpenCombineFoundation
|
||||
#endif
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
final class PropertyListEncoderTests: XCTestCase {
|
||||
|
||||
func testSuccessfullyEncode() {
|
||||
let encoder = PropertyListEncoder()
|
||||
encoder.outputFormat = .xml
|
||||
let input = [Subscribers.Completion<TestingError>.finished]
|
||||
var actualOutput: String?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
|
||||
let cancellable = Just(input)
|
||||
.encode(encoder: encoder)
|
||||
.map { String(decoding: $0, as: UTF8.self) }
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTAssertEqual(actualOutput, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" \
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
\t<dict>
|
||||
\t\t<key>success</key>
|
||||
\t\t<true/>
|
||||
\t</dict>
|
||||
</array>
|
||||
</plist>
|
||||
|
||||
""")
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
func testEncodingFailure() {
|
||||
let encoder = PropertyListEncoder()
|
||||
let input = Double.nan
|
||||
var actualOutput: String?
|
||||
var actualCompletion: Subscribers.Completion<Error>?
|
||||
let cancellable = Just(input)
|
||||
.encode(encoder: encoder)
|
||||
.map { String(decoding: $0, as: UTF8.self) }
|
||||
.sink(receiveCompletion: { actualCompletion = $0 },
|
||||
receiveValue: { actualOutput = $0 })
|
||||
|
||||
switch actualCompletion {
|
||||
case .finished?:
|
||||
XCTFail("Unexpected success")
|
||||
case .failure(EncodingError.invalidValue)?:
|
||||
XCTAssertNil(actualOutput)
|
||||
case .failure(let error)?:
|
||||
XCTFail("Unexpected failure received: \(error)")
|
||||
case nil:
|
||||
XCTFail("Expected completion")
|
||||
}
|
||||
cancellable.cancel()
|
||||
}
|
||||
}
|
||||
@@ -1,724 +0,0 @@
|
||||
//
|
||||
// URLSessionTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Sergej Jaskiewicz on 13.12.2019.
|
||||
//
|
||||
|
||||
// swiftlint:disable multiline_arguments
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
#if canImport(FoundationNetworking)
|
||||
import FoundationNetworking
|
||||
#endif
|
||||
|
||||
// We can't test it on non-Darwin platforms because swift-corelibs-foundation
|
||||
// doesn't allow us to override some URLSession methods that we need.
|
||||
//
|
||||
// As soon as https://github.com/apple/swift-corelibs-foundation/pull/2587 makes it
|
||||
// into a release, we can enable these tests on non-Darwin platforms.
|
||||
//
|
||||
// The publisher itself though should work alright on those platforms.
|
||||
#if canImport(Darwin)
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST
|
||||
import Combine
|
||||
#else
|
||||
import OpenCombine
|
||||
import OpenCombineFoundation
|
||||
#endif
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
final class URLSessionTests: XCTestCase {
|
||||
|
||||
private typealias TrackingSubscriber =
|
||||
TrackingSubscriberBase<(data: Data, response: URLResponse), URLError>
|
||||
|
||||
private let testURL = URL(string: "https://github.com")!
|
||||
|
||||
private let testRequest = URLRequest(url: URL(string: "https://github.com")!,
|
||||
cachePolicy: .reloadIgnoringCacheData,
|
||||
timeoutInterval: 42)
|
||||
|
||||
private let testData = Data("test data".utf8)
|
||||
|
||||
private let testResponse = URLResponse(url: URL(string: "https://example.com")!,
|
||||
mimeType: "text/markdown",
|
||||
expectedContentLength: 300,
|
||||
textEncodingName: "utf-8")
|
||||
|
||||
private let testError = URLError(.cannotParseResponse, userInfo: ["a" : 1])
|
||||
|
||||
private let unknownError = URLError(.unknown)
|
||||
|
||||
func testDataTaskPublisherFromURL() {
|
||||
let publisher = makePublisher(TestURLSession(testDataTask: .init()), testURL)
|
||||
let expectedRequest = URLRequest(url: testURL)
|
||||
XCTAssertEqual(publisher.request, expectedRequest)
|
||||
}
|
||||
|
||||
func testDataTaskPublisherFromRequest() {
|
||||
let publisher = makePublisher(TestURLSession(testDataTask: .init()), testRequest)
|
||||
XCTAssertEqual(publisher.request, testRequest)
|
||||
}
|
||||
|
||||
func testReceiveNothing() {
|
||||
testReceiveResult(nil, nil, nil,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(unknownError))])
|
||||
}
|
||||
|
||||
func testReceiveOnlyData() {
|
||||
testReceiveResult(testData, nil, nil,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(unknownError))])
|
||||
}
|
||||
|
||||
func testReceiveDataAndResponse() {
|
||||
testReceiveResult(testData, testResponse, nil,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.value((testData, testResponse)),
|
||||
.completion(.finished)])
|
||||
}
|
||||
|
||||
func testReceiveDataAndURLError() {
|
||||
testReceiveResult(testData, nil, testError,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(testError))])
|
||||
}
|
||||
|
||||
func testReceiveDataAndUnrelatedError() {
|
||||
testReceiveResult(testData, nil, TestingError.oops,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(unknownError))])
|
||||
}
|
||||
|
||||
func testReceiveOnlyResponse() {
|
||||
testReceiveResult(nil, testResponse, nil,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.value((Data(), testResponse)),
|
||||
.completion(.finished)])
|
||||
}
|
||||
|
||||
func testReceiveResponseAndURLError() {
|
||||
testReceiveResult(nil, testResponse, testError,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(testError))])
|
||||
}
|
||||
|
||||
func testReceiveResponseAndUnrelatedError() {
|
||||
testReceiveResult(nil, testResponse, TestingError.oops,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(unknownError))])
|
||||
}
|
||||
|
||||
func testReceiveOnlyURLError() {
|
||||
testReceiveResult(nil, nil, testError,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(testError))])
|
||||
}
|
||||
|
||||
func testReceiveOnlyUnrelatedError() {
|
||||
testReceiveResult(nil, nil, TestingError.oops,
|
||||
expected: [.subscription("DataTaskPublisher"),
|
||||
.completion(.failure(unknownError))])
|
||||
}
|
||||
|
||||
func testRequesting() throws {
|
||||
let dataTask = TestURLSessionDataTask()
|
||||
let session = TestURLSession(testDataTask: dataTask)
|
||||
let publisher = makePublisher(session, testRequest)
|
||||
var downstreamSubscription: Subscription?
|
||||
let tracking = TrackingSubscriber(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
tracking.assertHistoryEqual([.subscription("DataTaskPublisher")],
|
||||
valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [])
|
||||
XCTAssertEqual(session.history, [])
|
||||
|
||||
session.completeDataTasks(testData, testResponse, nil)
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).request(.max(2))
|
||||
try XCTUnwrap(downstreamSubscription).request(.max(1))
|
||||
|
||||
tracking.assertHistoryEqual([.subscription("DataTaskPublisher")],
|
||||
valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [.resume, .resume])
|
||||
XCTAssertEqual(session.history, [.dataTaskWithRequestAndCompletion(testRequest)])
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
|
||||
tracking.assertHistoryEqual([.subscription("DataTaskPublisher")],
|
||||
valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [.resume, .resume, .cancel])
|
||||
XCTAssertEqual(session.history, [.dataTaskWithRequestAndCompletion(testRequest)])
|
||||
|
||||
session.completeDataTasks(testData, testResponse, nil)
|
||||
|
||||
tracking.assertHistoryEqual([.subscription("DataTaskPublisher")],
|
||||
valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [.resume, .resume, .cancel])
|
||||
XCTAssertEqual(session.history, [.dataTaskWithRequestAndCompletion(testRequest)])
|
||||
}
|
||||
|
||||
func testCancelAlreadyCancelled() throws {
|
||||
let dataTask = TestURLSessionDataTask()
|
||||
let session = TestURLSession(testDataTask: dataTask)
|
||||
let publisher = makePublisher(session, testRequest)
|
||||
var downstreamSubscription: Subscription?
|
||||
let tracking = TrackingSubscriber(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
tracking.assertHistoryEqual([.subscription("DataTaskPublisher")],
|
||||
valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [])
|
||||
XCTAssertEqual(session.history, [])
|
||||
|
||||
try XCTUnwrap(downstreamSubscription).request(.max(1))
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
try XCTUnwrap(downstreamSubscription).request(.max(1))
|
||||
try XCTUnwrap(downstreamSubscription).cancel()
|
||||
|
||||
tracking.assertHistoryEqual([.subscription("DataTaskPublisher")],
|
||||
valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [.resume, .cancel])
|
||||
XCTAssertEqual(session.history, [.dataTaskWithRequestAndCompletion(testRequest)])
|
||||
}
|
||||
|
||||
func testCrashesOnZeroDemand() throws {
|
||||
let dataTask = TestURLSessionDataTask()
|
||||
let session = TestURLSession(testDataTask: dataTask)
|
||||
let publisher = makePublisher(session, testURL)
|
||||
var downstreamSubscription: Subscription?
|
||||
let tracking = TrackingSubscriber(
|
||||
receiveSubscription: { downstreamSubscription = $0 }
|
||||
)
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
try assertCrashes {
|
||||
try XCTUnwrap(downstreamSubscription).request(.none)
|
||||
}
|
||||
}
|
||||
|
||||
func testURLSessionSubscriptionReflection() throws {
|
||||
let dataTask = TestURLSessionDataTask()
|
||||
let session = TestURLSession(testDataTask: dataTask)
|
||||
let publisher = makePublisher(session, testURL)
|
||||
try testSubscriptionReflection(
|
||||
description: "DataTaskPublisher",
|
||||
customMirror: expectedChildren(
|
||||
("task", "nil"),
|
||||
("downstream", .contains("TrackingSubscriberBase")),
|
||||
("parent", .matches(String(describing: Optional(publisher)))),
|
||||
("demand", "max(0)")
|
||||
),
|
||||
playgroundDescription: "DataTaskPublisher",
|
||||
sut: publisher
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Generic tests
|
||||
|
||||
private func testReceiveResult(_ data: Data?,
|
||||
_ response: URLResponse?,
|
||||
_ error: Error?,
|
||||
expected: [TrackingSubscriber.Event]) {
|
||||
let dataTask = TestURLSessionDataTask()
|
||||
let session = TestURLSession(testDataTask: dataTask)
|
||||
let publisher = makePublisher(session, testRequest)
|
||||
let tracking = TrackingSubscriber(receiveSubscription: { $0.request(.max(1)) })
|
||||
publisher.subscribe(tracking)
|
||||
|
||||
tracking.assertHistoryEqual([.subscription("DataTaskPublisher")],
|
||||
valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [.resume])
|
||||
XCTAssertEqual(session.history, [.dataTaskWithRequestAndCompletion(testRequest)])
|
||||
|
||||
session.completeDataTasks(data, response, error)
|
||||
session.completeDataTasks(data, response, error)
|
||||
session.completeDataTasks(data, response, error)
|
||||
|
||||
tracking.assertHistoryEqual(expected, valueComparator: ==)
|
||||
XCTAssertEqual(dataTask.history, [.resume])
|
||||
XCTAssertEqual(session.history, [.dataTaskWithRequestAndCompletion(testRequest)])
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple mock URLSession that records its history and allows executing
|
||||
/// callbacks synchronously
|
||||
private class TestURLSession: URLSession {
|
||||
|
||||
enum Event: Equatable {
|
||||
case delegateQueue
|
||||
case delegate
|
||||
case configuration
|
||||
case getSessionDescription
|
||||
case setSessionDescription(String?)
|
||||
case finishTasksAndInvalidate
|
||||
case invalidateAndCancel
|
||||
case reset
|
||||
case flush
|
||||
case getTasksWithCompletionHandler
|
||||
case getAllTasks
|
||||
case dataTaskWithRequest(URLRequest)
|
||||
case dataTaskWithRequestAndCompletion(URLRequest)
|
||||
case dataTaskWithURL(URL)
|
||||
case dataTaskWithURLAndCompletion(URL)
|
||||
case uploadTaskWithRequestFromFile(URLRequest, URL)
|
||||
case uploadTaskWithRequestFromFileWithCompletion(URLRequest, URL)
|
||||
case uploadTaskWithRequestFromData(URLRequest, Data)
|
||||
case uploadTaskWithRequestFromDataWithCompletion(URLRequest, Data?)
|
||||
case uploadTaskWithStreamedRequest(URLRequest)
|
||||
case downloadTaskWithRequest(URLRequest)
|
||||
case downloadTaskWithRequestAndCompletion(URLRequest)
|
||||
case downloadTaskWithURL(URL)
|
||||
case downloadTaskWithURLAndCompletion(URL)
|
||||
case downloadTaskWithResumeData(Data)
|
||||
case downloadTaskWithResumeDataAndCompletion(Data)
|
||||
case streamTaskWithHostNameAndPort(String, Int)
|
||||
#if canImport(Darwin) && swift(>=5.1)
|
||||
case streamTaskWithService(NetService)
|
||||
case webSocketTaskWithURL(URL)
|
||||
case webSocketTaskWithURLAndProtocols(URL, [String])
|
||||
case webSocketTaskWithRequest(URLRequest)
|
||||
#endif // canImport(Darwin) && swift(>=5.1)
|
||||
}
|
||||
|
||||
private(set) var history = [Event]()
|
||||
|
||||
private(set) var dataTaskCompletionHandlers: [(Data?, URLResponse?, Error?) -> Void]
|
||||
|
||||
private let testDataTask: TestURLSessionDataTask
|
||||
|
||||
init(testDataTask: TestURLSessionDataTask) {
|
||||
self.testDataTask = testDataTask
|
||||
self.dataTaskCompletionHandlers = []
|
||||
}
|
||||
|
||||
// MARK: Testing
|
||||
|
||||
func completeDataTasks(_ data: Data?, _ response: URLResponse?, _ error: Error?) {
|
||||
for completionHandler in dataTaskCompletionHandlers {
|
||||
completionHandler(data, response, error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Overrides
|
||||
|
||||
override class var shared: URLSession { fatalError("shared session is unavailable") }
|
||||
|
||||
override var delegateQueue: OperationQueue {
|
||||
history.append(.delegateQueue)
|
||||
return super.delegateQueue
|
||||
}
|
||||
|
||||
override var delegate: URLSessionDelegate? {
|
||||
history.append(.delegate)
|
||||
return super.delegate
|
||||
}
|
||||
|
||||
override var configuration: URLSessionConfiguration {
|
||||
history.append(.configuration)
|
||||
return super.configuration
|
||||
}
|
||||
|
||||
override var sessionDescription: String? {
|
||||
get {
|
||||
history.append(.getSessionDescription)
|
||||
return super.sessionDescription
|
||||
}
|
||||
set {
|
||||
history.append(.setSessionDescription(newValue))
|
||||
super.sessionDescription = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override func finishTasksAndInvalidate() {
|
||||
history.append(.finishTasksAndInvalidate)
|
||||
super.finishTasksAndInvalidate()
|
||||
}
|
||||
|
||||
override func invalidateAndCancel() {
|
||||
history.append(.invalidateAndCancel)
|
||||
super.invalidateAndCancel()
|
||||
}
|
||||
|
||||
override func reset(completionHandler: @escaping () -> Void) {
|
||||
history.append(.reset)
|
||||
super.reset(completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func flush(completionHandler: @escaping () -> Void) {
|
||||
history.append(.flush)
|
||||
super.flush(completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func getTasksWithCompletionHandler(
|
||||
_ completionHandler: @escaping ([URLSessionDataTask],
|
||||
[URLSessionUploadTask],
|
||||
[URLSessionDownloadTask]) -> Void
|
||||
) {
|
||||
history.append(.getTasksWithCompletionHandler)
|
||||
super.getTasksWithCompletionHandler(completionHandler)
|
||||
}
|
||||
|
||||
@available(macOS 10.11, iOS 9.0, *)
|
||||
override func getAllTasks(completionHandler: @escaping ([URLSessionTask]) -> Void) {
|
||||
history.append(.getAllTasks)
|
||||
super.getAllTasks(completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func dataTask(with request: URLRequest) -> URLSessionDataTask {
|
||||
history.append(.dataTaskWithRequest(request))
|
||||
return testDataTask
|
||||
}
|
||||
|
||||
override func dataTask(with url: URL) -> URLSessionDataTask {
|
||||
history.append(.dataTaskWithURL(url))
|
||||
return testDataTask
|
||||
}
|
||||
|
||||
override func dataTask(
|
||||
with url: URL,
|
||||
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
|
||||
) -> URLSessionDataTask {
|
||||
history.append(.dataTaskWithURLAndCompletion(url))
|
||||
dataTaskCompletionHandlers.append(completionHandler)
|
||||
return testDataTask
|
||||
}
|
||||
|
||||
override func dataTask(
|
||||
with request: URLRequest,
|
||||
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
|
||||
) -> URLSessionDataTask {
|
||||
history.append(.dataTaskWithRequestAndCompletion(request))
|
||||
dataTaskCompletionHandlers.append(completionHandler)
|
||||
return testDataTask
|
||||
}
|
||||
|
||||
override func uploadTask(with request: URLRequest,
|
||||
fromFile fileURL: URL) -> URLSessionUploadTask {
|
||||
history.append(.uploadTaskWithRequestFromFile(request, fileURL))
|
||||
return super.uploadTask(with: request, fromFile: fileURL)
|
||||
}
|
||||
|
||||
override func uploadTask(with request: URLRequest,
|
||||
from bodyData: Data) -> URLSessionUploadTask {
|
||||
history.append(.uploadTaskWithRequestFromData(request, bodyData))
|
||||
return super.uploadTask(with: request, from: bodyData)
|
||||
}
|
||||
|
||||
override func uploadTask(
|
||||
withStreamedRequest request: URLRequest
|
||||
) -> URLSessionUploadTask {
|
||||
history.append(.uploadTaskWithStreamedRequest(request))
|
||||
return super.uploadTask(withStreamedRequest: request)
|
||||
}
|
||||
|
||||
override func uploadTask(
|
||||
with request: URLRequest,
|
||||
fromFile fileURL: URL,
|
||||
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
|
||||
) -> URLSessionUploadTask {
|
||||
history.append(.uploadTaskWithRequestFromFileWithCompletion(request, fileURL))
|
||||
return super.uploadTask(with: request,
|
||||
fromFile: fileURL,
|
||||
completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func uploadTask(
|
||||
with request: URLRequest,
|
||||
from bodyData: Data?,
|
||||
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
|
||||
) -> URLSessionUploadTask {
|
||||
history.append(.uploadTaskWithRequestFromDataWithCompletion(request, bodyData))
|
||||
return super.uploadTask(with: request,
|
||||
from: bodyData,
|
||||
completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func downloadTask(with request: URLRequest) -> URLSessionDownloadTask {
|
||||
history.append(.downloadTaskWithRequest(request))
|
||||
return super.downloadTask(with: request)
|
||||
}
|
||||
|
||||
override func downloadTask(with url: URL) -> URLSessionDownloadTask {
|
||||
history.append(.downloadTaskWithURL(url))
|
||||
return super.downloadTask(with: url)
|
||||
}
|
||||
|
||||
override func downloadTask(
|
||||
with request: URLRequest,
|
||||
completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void
|
||||
) -> URLSessionDownloadTask {
|
||||
history.append(.downloadTaskWithRequestAndCompletion(request))
|
||||
return super.downloadTask(with: request, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func downloadTask(
|
||||
with url: URL,
|
||||
completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void
|
||||
) -> URLSessionDownloadTask {
|
||||
history.append(.downloadTaskWithURLAndCompletion(url))
|
||||
return super.downloadTask(with: url, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func downloadTask(
|
||||
withResumeData resumeData: Data,
|
||||
completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void
|
||||
) -> URLSessionDownloadTask {
|
||||
history.append(.downloadTaskWithResumeDataAndCompletion(resumeData))
|
||||
return super.downloadTask(withResumeData: resumeData,
|
||||
completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
override func downloadTask(
|
||||
withResumeData resumeData: Data
|
||||
) -> URLSessionDownloadTask {
|
||||
history.append(.downloadTaskWithResumeData(resumeData))
|
||||
return super.downloadTask(withResumeData: resumeData)
|
||||
}
|
||||
|
||||
@available(macOS 10.11, iOS 9.0, *)
|
||||
override func streamTask(withHostName hostname: String,
|
||||
port: Int) -> URLSessionStreamTask {
|
||||
history.append(.streamTaskWithHostNameAndPort(hostname, port))
|
||||
return super.streamTask(withHostName: hostname, port: port)
|
||||
}
|
||||
|
||||
#if canImport(Darwin) && swift(>=5.1)
|
||||
@available(macOS 10.11, iOS 9.0, *)
|
||||
override func streamTask(with service: NetService) -> URLSessionStreamTask {
|
||||
history.append(.streamTaskWithService(service))
|
||||
return super.streamTask(with: service)
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
override func webSocketTask(with url: URL) -> URLSessionWebSocketTask {
|
||||
history.append(.webSocketTaskWithURL(url))
|
||||
return super.webSocketTask(with: url)
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
override func webSocketTask(with url: URL,
|
||||
protocols: [String]) -> URLSessionWebSocketTask {
|
||||
history.append(.webSocketTaskWithURLAndProtocols(url, protocols))
|
||||
return super.webSocketTask(with: url, protocols: protocols)
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
override func webSocketTask(with request: URLRequest) -> URLSessionWebSocketTask {
|
||||
history.append(.webSocketTaskWithRequest(request))
|
||||
return super.webSocketTask(with: request)
|
||||
}
|
||||
#endif // canImport(Darwin) && swift(>=5.1)
|
||||
}
|
||||
|
||||
private final class TestURLSessionDataTask: URLSessionDataTask {
|
||||
|
||||
enum Event: Equatable {
|
||||
case taskIdentifier
|
||||
case originalRequest
|
||||
case currentRequest
|
||||
case response
|
||||
case progress
|
||||
case getEarliestBeginDate
|
||||
case setEarliestBeginDate(Date?)
|
||||
case getCountOfBytesClientExpectsToSend
|
||||
case setCountOfBytesClientExpectsToSend(Int64)
|
||||
case getCountOfBytesClientExpectsToReceive
|
||||
case setCountOfBytesClientExpectsToReceive(Int64)
|
||||
case countOfBytesReceived
|
||||
case countOfBytesSent
|
||||
case countOfBytesExpectedToSend
|
||||
case countOfBytesExpectedToReceive
|
||||
case getTaskDescription
|
||||
case setTaskDescription(String?)
|
||||
case cancel
|
||||
case state
|
||||
case error
|
||||
case suspend
|
||||
case resume
|
||||
case getPriority
|
||||
case setPriority(Float)
|
||||
}
|
||||
|
||||
private(set) var history = [Event]()
|
||||
|
||||
override init() {}
|
||||
|
||||
override var taskIdentifier: Int {
|
||||
history.append(.taskIdentifier)
|
||||
return super.taskIdentifier
|
||||
}
|
||||
|
||||
override var originalRequest: URLRequest? {
|
||||
history.append(.originalRequest)
|
||||
return super.originalRequest
|
||||
}
|
||||
|
||||
override var currentRequest: URLRequest? {
|
||||
history.append(.currentRequest)
|
||||
return super.currentRequest
|
||||
}
|
||||
|
||||
override var response: URLResponse? {
|
||||
history.append(.response)
|
||||
return super.response
|
||||
}
|
||||
|
||||
@available(macOS 10.13, iOS 11.0, *)
|
||||
override var progress: Progress {
|
||||
history.append(.progress)
|
||||
return super.progress
|
||||
}
|
||||
|
||||
@available(macOS 10.13, iOS 11.0, *)
|
||||
override var earliestBeginDate: Date? {
|
||||
get {
|
||||
history.append(.getEarliestBeginDate)
|
||||
#if canImport(Darwin)
|
||||
return super.earliestBeginDate
|
||||
#else
|
||||
return nil // Deprecated in swift-corerlibs-foundation
|
||||
#endif
|
||||
}
|
||||
set {
|
||||
history.append(.setEarliestBeginDate(newValue))
|
||||
#if canImport(Darwin)
|
||||
super.earliestBeginDate = newValue
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.13, iOS 11.0, *)
|
||||
override var countOfBytesClientExpectsToSend: Int64 {
|
||||
get {
|
||||
history.append(.getCountOfBytesClientExpectsToSend)
|
||||
return super.countOfBytesClientExpectsToSend
|
||||
}
|
||||
set {
|
||||
history.append(.setCountOfBytesClientExpectsToSend(newValue))
|
||||
super.countOfBytesClientExpectsToSend = newValue
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.13, iOS 11.0, *)
|
||||
override var countOfBytesClientExpectsToReceive: Int64 {
|
||||
get {
|
||||
history.append(.getCountOfBytesClientExpectsToReceive)
|
||||
return super.countOfBytesClientExpectsToReceive
|
||||
}
|
||||
set {
|
||||
history.append(.setCountOfBytesClientExpectsToReceive(newValue))
|
||||
super.countOfBytesClientExpectsToReceive = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override var countOfBytesReceived: Int64 {
|
||||
history.append(.countOfBytesReceived)
|
||||
return super.countOfBytesReceived
|
||||
}
|
||||
|
||||
override var countOfBytesSent: Int64 {
|
||||
history.append(.countOfBytesSent)
|
||||
return super.countOfBytesSent
|
||||
}
|
||||
|
||||
override var countOfBytesExpectedToSend: Int64 {
|
||||
history.append(.countOfBytesExpectedToSend)
|
||||
return super.countOfBytesExpectedToSend
|
||||
}
|
||||
|
||||
override var countOfBytesExpectedToReceive: Int64 {
|
||||
history.append(.countOfBytesExpectedToReceive)
|
||||
return super.countOfBytesExpectedToReceive
|
||||
}
|
||||
|
||||
override var taskDescription: String? {
|
||||
get {
|
||||
history.append(.getTaskDescription)
|
||||
return super.taskDescription
|
||||
}
|
||||
set {
|
||||
history.append(.setTaskDescription(newValue))
|
||||
super.taskDescription = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override func cancel() {
|
||||
history.append(.cancel)
|
||||
}
|
||||
|
||||
override var state: URLSessionTask.State {
|
||||
history.append(.state)
|
||||
return super.state
|
||||
}
|
||||
|
||||
override var error: Error? {
|
||||
history.append(.error)
|
||||
return super.error
|
||||
}
|
||||
|
||||
override func suspend() {
|
||||
history.append(.suspend)
|
||||
}
|
||||
|
||||
override func resume() {
|
||||
history.append(.resume)
|
||||
}
|
||||
|
||||
override var priority: Float {
|
||||
get {
|
||||
history.append(.getPriority)
|
||||
return super.priority
|
||||
}
|
||||
set {
|
||||
history.append(.setPriority(newValue))
|
||||
super.priority = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension URLError: EquatableError {}
|
||||
|
||||
#if OPENCOMBINE_COMPATIBILITY_TEST || !canImport(Combine)
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
private func makePublisher(
|
||||
_ session: URLSession,
|
||||
_ url: URL
|
||||
) -> URLSession.DataTaskPublisher {
|
||||
return session.dataTaskPublisher(for: url)
|
||||
}
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, *)
|
||||
private func makePublisher(
|
||||
_ session: URLSession,
|
||||
_ request: URLRequest
|
||||
) -> URLSession.DataTaskPublisher {
|
||||
return session.dataTaskPublisher(for: request)
|
||||
}
|
||||
#else
|
||||
private func makePublisher(
|
||||
_ session: URLSession,
|
||||
_ url: URL
|
||||
) -> URLSession.OCombine.DataTaskPublisher {
|
||||
return session.ocombine.dataTaskPublisher(for: url)
|
||||
}
|
||||
|
||||
private func makePublisher(
|
||||
_ session: URLSession,
|
||||
_ request: URLRequest
|
||||
) -> URLSession.OCombine.DataTaskPublisher {
|
||||
return session.ocombine.dataTaskPublisher(for: request)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // canImport(Darwin)
|
||||
@@ -82,6 +82,15 @@ extension XCTest {
|
||||
printDiagostics()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@available(macOS 10.13, iOS 8.0, *)
|
||||
func assertCrashesOnDarwin(within body: () -> Void) {
|
||||
#if canImport(Darwin)
|
||||
assertCrashes(within: body)
|
||||
#else
|
||||
body()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user