diff --git a/Foundation/Data.swift b/Foundation/Data.swift index 0c0ede6e10..9eec949f6f 100644 --- a/Foundation/Data.swift +++ b/Foundation/Data.swift @@ -196,6 +196,13 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public init(buffer: UnsafeBufferPointer) { _wrapped = _SwiftNSData(immutableObject: NSData(bytes: buffer.baseAddress, length: MemoryLayout.stride * buffer.count)) } + + /// Initialize a `Data` with copied memory content. + /// + /// - parameter buffer: A buffer pointer to copy. The size is calculated from `SourceType` and `buffer.count`. + public init(buffer: UnsafeMutableBufferPointer) { + _wrapped = _SwiftNSData(immutableObject: NSData(bytes: UnsafePointer(buffer.baseAddress), length: MemoryLayout.stride * buffer.count)) + } /// Initialize a `Data` with the contents of an Array. /// @@ -295,11 +302,18 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - internal init(_bridged data: NSData) { + /// Initialize a `Data` by adopting a reference type. + /// + /// You can use this initializer to create a `struct Data` that wraps a `class NSData`. `struct Data` will use the `class NSData` for all operations. Other initializers (including casting using `as Data`) may choose to hold a reference or not, based on a what is the most efficient representation. + /// + /// If the resulting value is mutated, then `Data` will invoke the `mutableCopy()` function on the reference to copy the contents. You may customize the behavior of that function if you wish to return a specialized mutable subclass. + /// + /// - parameter reference: The instance of `NSData` that you wish to wrap. This instance will be copied by `struct Data`. + public init(referencing reference: NSData) { // We must copy the input because it might be mutable; just like storing a value type in ObjC - _wrapped = _SwiftNSData(immutableObject: data.copy() as! NSObject) + _wrapped = _SwiftNSData(immutableObject: reference.copy() as! NSObject) } - + // ----------------------------------- // MARK: - Properties and Functions @@ -639,11 +653,11 @@ extension Data { } public static func _forceBridgeFromObjectiveC(_ input: NSData, result: inout Data?) { - result = Data(_bridged: input) + result = Data(referencing: input) } public static func _conditionallyBridgeFromObjectiveC(_ input: NSData, result: inout Data?) -> Bool { - result = Data(_bridged: input) + result = Data(referencing: input) return true } diff --git a/Foundation/NSData.swift b/Foundation/NSData.swift index 7a85117e0d..83eac0ed40 100644 --- a/Foundation/NSData.swift +++ b/Foundation/NSData.swift @@ -394,7 +394,7 @@ extension NSData { return Data() } if range.location == 0 && range.length == self.length { - return Data(_bridged: self) + return Data(referencing: self) } return Data(bytes: bytes.advanced(by: range.location), count: range.length) } @@ -582,7 +582,7 @@ extension NSData { extension NSData : _CFBridgable, _SwiftBridgable { typealias SwiftType = Data - internal var _swiftObject: SwiftType { return Data(_bridged: self) } + internal var _swiftObject: SwiftType { return Data(referencing: self) } public func bridge() -> Data { return _swiftObject @@ -604,7 +604,7 @@ extension CFData : _NSBridgable, _SwiftBridgable { typealias NSType = NSData typealias SwiftType = Data internal var _nsObject: NSType { return unsafeBitCast(self, to: NSType.self) } - internal var _swiftObject: SwiftType { return Data(_bridged: self._nsObject) } + internal var _swiftObject: SwiftType { return Data(referencing: self._nsObject) } } extension NSMutableData { diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 7d6c39867d..d62d3035f0 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -44,6 +44,9 @@ class TestNSData: XCTestCase { ("test_replaceBytes", test_replaceBytes), ("test_initDataWithCapacity", test_initDataWithCapacity), ("test_initDataWithCount", test_initDataWithCount), + ("test_initDataWithUnsafeBuffer", test_initDataWithUnsafeBuffer), + ("test_initDataWithUnsafeMutableBuffer", test_initDataWithUnsafeMutableBuffer), + ("test_initDataWithReference", test_initDataWithReference), ] } @@ -393,5 +396,42 @@ class TestNSData: XCTestCase { return } } -} + func test_initDataWithUnsafeBuffer() { + var srcArray: [UInt8] = [0, 1, 2, 3, 4, 5] + let buffer = UnsafeBufferPointer(start: &srcArray, + count: srcArray.count) + let data = Data(buffer: buffer) + + XCTAssertEqual(data.count, srcArray.count * MemoryLayout.stride) + XCTAssertTrue(Data(bytes: srcArray) == data) + } + + func test_initDataWithUnsafeMutableBuffer() { + var srcArray: [UInt16] = [0, 1, 2, 3, 4, 5] + let buffer = UnsafeMutableBufferPointer(start: &srcArray, + count: srcArray.count) + let data = Data(buffer: buffer) + + XCTAssertEqual(data.count, buffer.count * MemoryLayout.stride) + + // Mutate the original array to test the new instance is a copy + srcArray[0] = 0xffff + XCTAssertEqual(buffer[0], srcArray[0]) + XCTAssertNotEqual(data[0], 0xff) + XCTAssertEqual(data[0], 0x00) + } + + func test_initDataWithReference() { + let srcArray: [UInt8] = [1, 2, 3, 4, 5] + let srcNSData = NSData(bytes: srcArray, length: srcArray.count) + var data = Data(referencing: srcNSData) + + XCTAssertTrue(data == Data(bytes: srcArray)) + XCTAssertEqual(srcNSData.length, srcArray.count) + + // Mutate the original array to test the new instance is a copy + data.resetBytes(in: data.startIndex..