Skip to content
Closed
48 changes: 29 additions & 19 deletions Foundation.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Foundation/URLSession/HTTPBodySource.swift - URLSession & libcurl
// Foundation/URLSession/BodySource.swift - URLSession & libcurl
//
// This source file is part of the Swift.org open source project
//
Expand All @@ -20,35 +20,35 @@ import CoreFoundation
import Dispatch


/// Turn `NSData` into `dispatch_data_t`
/// Turn `Data` into `DispatchData`
internal func createDispatchData(_ data: Data) -> DispatchData {
//TODO: Avoid copying data
let buffer = UnsafeRawBufferPointer(start: data._backing.bytes,
count: data.count)
return DispatchData(bytes: buffer)
}

/// Copy data from `dispatch_data_t` into memory pointed to by an `UnsafeMutableBufferPointer`.
/// Copy data from `DispatchData` into memory pointed to by an `UnsafeMutableBufferPointer`.
internal func copyDispatchData<T>(_ data: DispatchData, infoBuffer buffer: UnsafeMutableBufferPointer<T>) {
precondition(data.count <= (buffer.count * MemoryLayout<T>.size))
_ = data.copyBytes(to: buffer)
}

/// Split `dispatch_data_t` into `(head, tail)` pair.
/// Split `DispatchData` into `(head, tail)` pair.
internal func splitData(dispatchData data: DispatchData, atPosition position: Int) -> (DispatchData,DispatchData) {
return (data.subdata(in: 0..<position), data.subdata(in: position..<data.count))
}

/// A (non-blocking) source for HTTP body data.
internal protocol _HTTPBodySource: class {
/// A (non-blocking) source for body data.
internal protocol _BodySource: class {
/// Get the next chunck of data.
///
/// - Returns: `.data` until the source is exhausted, at which point it will
/// return `.done`. Since this is non-blocking, it will return `.retryLater`
/// if no data is available at this point, but will be available later.
func getNextChunk(withLength length: Int) -> _HTTPBodySourceDataChunk
func getNextChunk(withLength length: Int) -> _BodySourceDataChunk
}
internal enum _HTTPBodySourceDataChunk {
internal enum _BodySourceDataChunk {
case data(DispatchData)
/// The source is depleted.
case done
Expand All @@ -57,26 +57,26 @@ internal enum _HTTPBodySourceDataChunk {
case error
}

/// A HTTP body data source backed by `dispatch_data_t`.
internal final class _HTTPBodyDataSource {
var data: DispatchData!
/// A body data source backed by `DispatchData`.
internal final class _BodyDataSource {
var data: DispatchData!
init(data: DispatchData) {
self.data = data
}
}

extension _HTTPBodyDataSource : _HTTPBodySource {
extension _BodyDataSource : _BodySource {
enum _Error : Error {
case unableToRewindData
}

func getNextChunk(withLength length: Int) -> _HTTPBodySourceDataChunk {
func getNextChunk(withLength length: Int) -> _BodySourceDataChunk {
let remaining = data.count
if remaining == 0 {
return .done
} else if remaining <= length {
let r: DispatchData! = data
data = DispatchData.empty
data = DispatchData.empty
return .data(r)
} else {
let (chunk, remainder) = splitData(dispatchData: data, atPosition: length)
Expand All @@ -87,21 +87,21 @@ extension _HTTPBodyDataSource : _HTTPBodySource {
}


/// A HTTP body data source backed by a file.
/// A body data source backed by a file.
///
/// This allows non-blocking streaming of file data to the remote server.
///
/// The source reads data using a `dispatch_io_t` channel, and hence reading
/// The source reads data using a `DispatchIO` channel, and hence reading
/// file data is non-blocking. It has a local buffer that it fills as calls
/// to `getNextChunk(withLength:)` drain it.
///
/// - Note: Calls to `getNextChunk(withLength:)` and callbacks from libdispatch
/// should all happen on the same (serial) queue, and hence this code doesn't
/// have to be thread safe.
internal final class _HTTPBodyFileSource {
internal final class _BodyFileSource {
fileprivate let fileURL: URL
fileprivate let channel: DispatchIO
fileprivate let workQueue: DispatchQueue
fileprivate let channel: DispatchIO
fileprivate let workQueue: DispatchQueue
fileprivate let dataAvailableHandler: () -> Void
fileprivate var hasActiveReadHandler = false
fileprivate var availableChunk: _Chunk = .empty
Expand Down Expand Up @@ -146,7 +146,7 @@ internal final class _HTTPBodyFileSource {
}
}

fileprivate extension _HTTPBodyFileSource {
fileprivate extension _BodyFileSource {
fileprivate var desiredBufferLength: Int { return 3 * CFURLSessionMaxWriteSize }
/// Enqueue a dispatch I/O read to fill the buffer.
///
Expand All @@ -158,7 +158,7 @@ fileprivate extension _HTTPBodyFileSource {
guard availableByteCount < desiredBufferLength else { return }
guard !hasActiveReadHandler else { return } // We're already reading
hasActiveReadHandler = true

let lengthToRead = desiredBufferLength - availableByteCount
channel.read(offset: 0, length: lengthToRead, queue: workQueue) { (done: Bool, data: DispatchData?, errno: Int32) in
let wasEmpty = self.availableByteCount == 0
Expand All @@ -176,7 +176,7 @@ fileprivate extension _HTTPBodyFileSource {
default:
fatalError("Invalid arguments to read(3) callback.")
}

if wasEmpty && (0 < self.availableByteCount) {
self.dataAvailableHandler()
}
Expand Down Expand Up @@ -208,8 +208,8 @@ fileprivate extension _HTTPBodyFileSource {
}
}

extension _HTTPBodyFileSource : _HTTPBodySource {
func getNextChunk(withLength length: Int) -> _HTTPBodySourceDataChunk {
extension _BodyFileSource : _BodySource {
func getNextChunk(withLength length: Int) -> _BodySourceDataChunk {
switch availableChunk {
case .empty:
readNextChunk()
Expand All @@ -222,7 +222,7 @@ extension _HTTPBodyFileSource : _HTTPBodySource {

availableChunk = tail.isEmpty ? .empty : .data(tail)
readNextChunk()

if head.isEmpty {
return .retryLater
} else {
Expand Down
Loading