From 6522414c9aa6cbe89f05786e98ab63bf6ebe4f73 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Tue, 16 Aug 2016 18:06:14 -0700 Subject: [PATCH 01/12] Swift 3 API Parity: Data & NSData Brings the Data and NSData API in s-cl-f to match the public version of Swift3 on Darwin. Reorganizes the implementation of Data/NSData to be a bit saner (versus somewhat vestigially matching the Objective-C categories on Darwin). Also brings a whole host of tests over from our overlay (some disabled for now, since the real focus of this PR is the API parity, which we should have now). --- Foundation/Data.swift | 214 +++-- Foundation/NSConcreteValue.swift | 3 +- Foundation/NSData.swift | 666 ++++++++------- Foundation/NSFileHandle.swift | 2 +- Foundation/NSFileManager.swift | 2 +- Foundation/NSString.swift | 6 +- Foundation/NSXMLDocument.swift | 2 +- Foundation/NSXMLParser.swift | 6 +- TestFoundation/TestNSData.swift | 819 ++++++++++++++++++- TestFoundation/TestNSJSONSerialization.swift | 7 +- TestFoundation/TestNSXMLParser.swift | 4 +- 11 files changed, 1286 insertions(+), 445 deletions(-) diff --git a/Foundation/Data.swift b/Foundation/Data.swift index 0c0ede6e10..17e54aee3e 100644 --- a/Foundation/Data.swift +++ b/Foundation/Data.swift @@ -73,56 +73,15 @@ internal final class _SwiftNSData : NSData, _SwiftNativeFoundationType { releaseWrappedObject() } - // Stubs - // ----- - + // MARK: - Funnel overrides override var length : Int { get { return _mapUnmanaged { $0.length } } } - override var bytes : UnsafeRawPointer { return _mapUnmanaged { $0.bytes } } - -// override func subdata(with range: NSRange) -> Data { -// return _mapUnmanaged { $0.subdata(with: range) } -// } -// -// override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { -// return _mapUnmanaged { $0.getBytes(buffer, length: length) } -// } -// -// override func getBytes(_ buffer: UnsafeMutableRawPointer, range: NSRange) { -// return _mapUnmanaged { $0.getBytes(buffer, range: range) } -// } -// -// override func isEqual(to other: Data) -> Bool { -// return _mapUnmanaged { return $0.isEqual(to: other) } -// } -// -// override func write(to url: URL, options: Data.WritingOptions) throws { -// return try _mapUnmanaged { try $0.write(to: url, options: options) } -// } -// -// override func range(of data: Data, options: Data.SearchOptions, range: NSRange) -> NSRange { -// return _mapUnmanaged { -// $0.range(of: data, options: options, in: range) -// } -// } -// -// override func enumerateByteRanges(using block: (UnsafeRawPointer, NSRange, UnsafeMutablePointer) -> Void) { -// return _mapUnmanaged { $0.enumerateBytes(block) } -// } -// -// override func base64EncodedString(options: Data.Base64EncodingOptions) -> String { -// return _mapUnmanaged { $0.base64EncodedString(options) } -// } -// -// override func base64EncodedData(options: Data.Base64EncodingOptions) -> Data { -// return _mapUnmanaged { $0.base64EncodedData(options) } -// } } /** @@ -197,10 +156,17 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H _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. /// /// - parameter bytes: An array of bytes to copy. - public init(bytes: Array) { + public init(bytes: [UInt8]) { _wrapped = bytes.withUnsafeBufferPointer { return _SwiftNSData(immutableObject: NSData(bytes: $0.baseAddress, length: $0.count)) } @@ -226,7 +192,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// - parameter capacity: The size of the data. public init(capacity: Int) { if let d = NSMutableData(capacity: capacity) { - _wrapped = _SwiftNSData(immutableObject: d) + _wrapped = _SwiftNSData(mutableObject: d) } else { fatalError("Unable to allocate data of the requested capacity") } @@ -261,7 +227,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// Returns nil when the input is not recognized as valid Base-64. /// - parameter base64String: The string to parse. - /// - parameter options: Decoding options. Default value is `[]`. + /// - parameter options: Encoding options. Default value is `[]`. public init?(base64Encoded base64String: String, options: Data.Base64DecodingOptions = []) { if let d = NSData(base64Encoded: base64String, options: Base64DecodingOptions(rawValue: options.rawValue)) { _wrapped = _SwiftNSData(immutableObject: d) @@ -288,16 +254,23 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// - parameter count: The number of bytes the data initially contains. public init(count: Int) { - if let memory = calloc(1, count)?.bindMemory(to: UInt8.self, capacity: count) { - self.init(bytesNoCopy: memory, count: count, deallocator: .free) + if let d = NSMutableData(length: count) { + _wrapped = _SwiftNSData(mutableObject: d) } else { fatalError("Unable to allocate data of the requested count") } } + - internal init(_bridged data: 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) + /// 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) { + _wrapped = _SwiftNSData(immutableObject: reference.copy() as AnyObject) } // ----------------------------------- @@ -313,8 +286,12 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - private func _getUnsafeBytesPointer() -> UnsafeRawPointer { - return _mapUnmanaged { return $0.bytes } + private func _getUnsafeBytesPointer() -> UnsafePointer { + + return _mapUnmanaged { + let boundBytes = $0.bytes.bindMemory(to: Void.self, capacity: $0.length) + return boundBytes + } } /// Access the bytes in the data. @@ -323,13 +300,15 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public func withUnsafeBytes(_ body: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { let bytes = _getUnsafeBytesPointer() defer { _fixLifetime(self)} - let contentPtr = bytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) - return try body(contentPtr) + let result = try bytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { + return try body($0) + } + return result } - private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutableRawPointer { + private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutablePointer { return _applyUnmanagedMutation { - return $0.mutableBytes + return $0.mutableBytes.bindMemory(to: Void.self, capacity: count) } } @@ -340,8 +319,11 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { let mutableBytes = _getUnsafeMutableBytesPointer() defer { _fixLifetime(self)} - let contentPtr = mutableBytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) - return try body(contentPtr) + + let result = try mutableBytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { + return try body($0) + } + return result } // MARK: - @@ -356,7 +338,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H _mapUnmanaged { $0.getBytes(pointer, length: count) } } - private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: NSRange) { + private func _copyBytesHelper(to pointer: UnsafeMutablePointer, from range: NSRange) { _mapUnmanaged { $0.getBytes(pointer, range: range) } } @@ -366,7 +348,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// - parameter range: The range in the `Data` to copy. /// - warning: This method does not verify that the contents at pointer have enough space to hold the required number of bytes. public func copyBytes(to pointer: UnsafeMutablePointer, from range: Range) { - _copyBytesHelper(to: pointer, from: NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)) + _copyBytesHelper(to: pointer, from: NSRange(range)) } /// Copy the contents of the data into a buffer. @@ -397,8 +379,10 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H guard !copyRange.isEmpty else { return 0 } let nsRange = NSMakeRange(copyRange.lowerBound, copyRange.upperBound - copyRange.lowerBound) - let pointer = UnsafeMutableRawPointer(buffer.baseAddress!) - _copyBytesHelper(to: pointer, from: nsRange) + let ptr = buffer.baseAddress! + ptr.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { + _copyBytesHelper(to: $0, from: nsRange) + } return copyRange.count } @@ -499,10 +483,13 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// Replace a region of bytes in the data with new data. /// - /// - parameter range: The range in the data to replace. + /// This will resize the data if required, to fit the entire contents of `data`. + /// + /// - precondition: The bounds of `subrange` must be valid indices of the collection. + /// - parameter subrange: The range in the data to replace. If `subrange.lowerBound == data.count && subrange.count == 0` then this operation is an append. /// - parameter data: The replacement data. - public mutating func replaceBytes(in range: Range, with data: Data) { - let nsRange = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound) + public mutating func replaceSubrange(_ subrange: Range, with data: Data) { + let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound) let cnt = data.count let bytes = data._getUnsafeBytesPointer() @@ -511,6 +498,68 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } + /// Replace a region of bytes in the data with new bytes from a buffer. + /// + /// This will resize the data if required, to fit the entire contents of `buffer`. + /// + /// - precondition: The bounds of `subrange` must be valid indices of the collection. + /// - parameter subrange: The range in the data to replace. + /// - parameter buffer: The replacement bytes. + public mutating func replaceSubrange(_ subrange: Range, with buffer: UnsafeBufferPointer) { + let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound) + let bufferCount = buffer.count * MemoryLayout.stride + + _applyUnmanagedMutation { + $0.replaceBytes(in: nsRange, withBytes: buffer.baseAddress!, length: bufferCount) + } + + } + + /// Replace a region of bytes in the data with new bytes from a collection. + /// + /// This will resize the data if required, to fit the entire contents of `newElements`. + /// + /// - precondition: The bounds of `subrange` must be valid indices of the collection. + /// - parameter subrange: The range in the data to replace. + /// - parameter newElements: The replacement bytes. + public mutating func replaceSubrange(_ subrange: Range, with newElements: ByteCollection) where ByteCollection.Iterator.Element == Data.Iterator.Element { + + // Calculate this once, it may not be O(1) + let replacementCount : Int = numericCast(newElements.count) + let currentCount = self.count + let subrangeCount = subrange.count + + if currentCount < subrange.lowerBound + subrangeCount { + if subrangeCount == 0 { + preconditionFailure("location \(subrange.lowerBound) exceeds data count \(currentCount)") + } else { + preconditionFailure("range \(subrange) exceeds data count \(currentCount)") + } + } + + let resultCount = currentCount - subrangeCount + replacementCount + if resultCount != currentCount { + // This may realloc. + // In the future, if we keep the malloced pointer and count inside this struct/ref instead of deferring to NSData, we may be able to do this more efficiently. + self.count = resultCount + } + + let shift = resultCount - currentCount + let start = subrange.lowerBound + + self.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) -> () in + if shift != 0 { + let destination = bytes + start + replacementCount + let source = bytes + start + subrangeCount + memmove(destination, source, currentCount - start - subrangeCount) + } + + if replacementCount != 0 { + newElements._copyContents(initializing: bytes + start) + } + } + } + /// Return a new copy of the data in a specified range. /// /// - parameter range: The range to copy. @@ -526,16 +575,16 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// - parameter options: The options to use for the encoding. Default value is `[]`. /// - returns: The Base-64 encoded string. - public func base64EncodedString(_ options: Data.Base64EncodingOptions = []) -> String { - return _mapUnmanaged { $0.base64EncodedString(options) } + public func base64EncodedString(options: Data.Base64EncodingOptions = []) -> String { + return _mapUnmanaged { $0.base64EncodedString(options: options) } } /// Returns a Base-64 encoded `Data`. /// /// - parameter options: The options to use for the encoding. Default value is `[]`. /// - returns: The Base-64 encoded data. - public func base64EncodedData(_ options: Data.Base64EncodingOptions = []) -> Data { - return _mapUnmanaged { $0.base64EncodedData(options) } + public func base64EncodedData(options: Data.Base64EncodingOptions = []) -> Data { + return _mapUnmanaged { $0.base64EncodedData(options: options) } } // MARK: - @@ -556,7 +605,6 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H return _mapUnmanaged { $0.debugDescription } } - // MARK: - // MARK: - // MARK: Index and Subscript @@ -577,20 +625,12 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - public subscript(bounds: Range) -> MutableRandomAccessSlice { + public subscript(bounds: Range) -> MutableRandomAccessSlice { get { return MutableRandomAccessSlice(base: self, bounds: bounds) } set { - // Ideally this would be: - // replaceBytes(in: bounds, with: newValue._base) - // but we do not have access to _base due to 'internal' protection - // TODO: Use a custom Slice type so we have access to the underlying data - let arrayOfBytes = newValue.map { $0 } - arrayOfBytes.withUnsafeBufferPointer { - let otherData = Data(buffer: $0) - replaceBytes(in: bounds, with: otherData) - } + replaceSubrange(bounds, with: newValue.base) } } @@ -620,12 +660,14 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public func makeIterator() -> Data.Iterator { return IndexingIterator(_elements: self) } + + /// Returns `true` if the two `Data` arguments are equal. + public static func ==(d1 : Data, d2 : Data) -> Bool { + return d1._wrapped.isEqual(to: d2) + } } -/// Returns `true` if the two `Data` arguments are equal. -public func ==(d1 : Data, d2 : Data) -> Bool { - return d1._wrapped.isEqual(to: d2) -} + /// Provides bridging functionality for struct Data to class NSData and vice-versa. extension Data { @@ -639,11 +681,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/NSConcreteValue.swift b/Foundation/NSConcreteValue.swift index 41dead6562..2eacc68748 100644 --- a/Foundation/NSConcreteValue.swift +++ b/Foundation/NSConcreteValue.swift @@ -113,7 +113,8 @@ internal class NSConcreteValue : NSValue { } override var description : String { - return Data(bytes: self.value, count: self._size).description + let boundBytes = self.value.bindMemory(to: UInt8.self, capacity: self._size) + return Data(bytes: boundBytes, count: self._size).description } convenience required init?(coder aDecoder: NSCoder) { diff --git a/Foundation/NSData.swift b/Foundation/NSData.swift index 9659815178..5c55696478 100644 --- a/Foundation/NSData.swift +++ b/Foundation/NSData.swift @@ -16,22 +16,21 @@ import Glibc #endif extension NSData { - public struct ReadingOptions : OptionSet { public let rawValue : UInt public init(rawValue: UInt) { self.rawValue = rawValue } - public static let dataReadingMappedIfSafe = ReadingOptions(rawValue: UInt(1 << 0)) - public static let dataReadingUncached = ReadingOptions(rawValue: UInt(1 << 1)) - public static let dataReadingMappedAlways = ReadingOptions(rawValue: UInt(1 << 2)) + public static let mappedIfSafe = ReadingOptions(rawValue: UInt(1 << 0)) + public static let uncached = ReadingOptions(rawValue: UInt(1 << 1)) + public static let alwaysMapped = ReadingOptions(rawValue: UInt(1 << 2)) } public struct WritingOptions : OptionSet { public let rawValue : UInt public init(rawValue: UInt) { self.rawValue = rawValue } - public static let dataWritingAtomic = WritingOptions(rawValue: UInt(1 << 0)) - public static let dataWritingWithoutOverwriting = WritingOptions(rawValue: UInt(1 << 1)) + public static let atomic = WritingOptions(rawValue: UInt(1 << 0)) + public static let withoutOverwriting = WritingOptions(rawValue: UInt(1 << 1)) } public struct SearchOptions : OptionSet { @@ -46,10 +45,10 @@ extension NSData { public let rawValue : UInt public init(rawValue: UInt) { self.rawValue = rawValue } - public static let encoding64CharacterLineLength = Base64EncodingOptions(rawValue: UInt(1 << 0)) - public static let encoding76CharacterLineLength = Base64EncodingOptions(rawValue: UInt(1 << 1)) - public static let encodingEndLineWithCarriageReturn = Base64EncodingOptions(rawValue: UInt(1 << 4)) - public static let encodingEndLineWithLineFeed = Base64EncodingOptions(rawValue: UInt(1 << 5)) + public static let lineLength64Characters = Base64EncodingOptions(rawValue: UInt(1 << 0)) + public static let lineLength76Characters = Base64EncodingOptions(rawValue: UInt(1 << 1)) + public static let endLineWithCarriageReturn = Base64EncodingOptions(rawValue: UInt(1 << 4)) + public static let endLineWithLineFeed = Base64EncodingOptions(rawValue: UInt(1 << 5)) } public struct Base64DecodingOptions : OptionSet { @@ -74,6 +73,7 @@ private let __kCFAllocatesCollectable: CFOptionFlags = 0x20 open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { typealias CFType = CFData + private var _base = _CFInfo(typeID: CFDataGetTypeID()) private var _length: CFIndex = 0 private var _capacity: CFIndex = 0 @@ -90,33 +90,11 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } - public override required convenience init() { - let dummyPointer = unsafeBitCast(NSData.self, to: UnsafeMutableRawPointer.self) - self.init(bytes: dummyPointer, length: 0, copy: false, deallocator: nil) - } - - open override var hash: Int { - return Int(bitPattern: CFHash(_cfObject)) - } - - open override func isEqual(_ object: AnyObject?) -> Bool { - if let data = object as? NSData { - return self.isEqual(to: data._swiftObject) - } else { - return false - } - } - - deinit { - if let allocatedBytes = _bytes { - _deallocHandler?.handler(allocatedBytes, _length) - } - if type(of: self) === NSData.self || type(of: self) === NSMutableData.self { - _CFDeinit(self._cfObject) - } + override open var _cfTypeID: CFTypeID { + return CFDataGetTypeID() } - internal init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { + public init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { super.init() let options : CFOptionFlags = (type(of: self) == NSMutableData.self) ? __kCFMutable | __kCFGrowable : 0x0 let bytePtr = bytes?.bindMemory(to: UInt8.self, capacity: length) @@ -134,13 +112,132 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } + public override convenience init() { + let dummyPointer = unsafeBitCast(NSData.self, to: UnsafeMutableRawPointer.self) + self.init(bytes: dummyPointer, length: 0, copy: false, deallocator: nil) + } + + public convenience init(bytes: UnsafeRawPointer?, length: Int) { + self.init(bytes: UnsafeMutableRawPointer(mutating: bytes), length: length, copy: true, deallocator: nil) + } + + public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int) { + self.init(bytes: bytes, length: length, copy: false, deallocator: nil) + } + + public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, freeWhenDone b: Bool) { + self.init(bytes: bytes, length: length, copy: false) { buffer, length in + if b { + free(buffer) + } + } + } + + public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { + self.init(bytes: bytes, length: length, copy: false, deallocator: deallocator) + } + public convenience init(contentsOfFile path: String, options readOptionsMask: ReadingOptions = []) throws { + let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: readOptionsMask) + self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) + } + + public convenience init?(contentsOfFile path: String) { + do { + let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: []) + self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) + } catch { + return nil + } + } + + public convenience init(data: Data) { + self.init(bytes:data._nsObject.bytes, length: data.count) + } + + public convenience init(contentsOf url: URL, options readOptionsMask: ReadingOptions = []) throws { + if url.isFileURL { + try self.init(contentsOfFile: url.path!, options: readOptionsMask) + } else { + let session = URLSession(configuration: URLSessionConfiguration.defaultSessionConfiguration()) + let cond = NSCondition() + var resError: NSError? + var resData: Data? + let task = session.dataTaskWithURL(url, completionHandler: { (data: Data?, response: URLResponse?, error: NSError?) -> Void in + resData = data + resError = error + cond.broadcast() + }) + task.resume() + cond.wait() + if resData == nil { + throw resError! + } + self.init(data: resData!) + } + } + + public convenience init?(base64Encoded base64String: String, options: Base64DecodingOptions = []) { + let encodedBytes = Array(base64String.utf8) + guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { + return nil + } + self.init(bytes: decodedBytes, length: decodedBytes.count) + } + + + /* Create an NSData from a Base-64, UTF-8 encoded NSData. By default, returns nil when the input is not recognized as valid Base-64. + */ + public convenience init?(base64Encoded base64Data: Data, options: Base64DecodingOptions = []) { + var encodedBytes = [UInt8](repeating: 0, count: base64Data.count) + base64Data._nsObject.getBytes(&encodedBytes, length: encodedBytes.count) + guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { + return nil + } + self.init(bytes: decodedBytes, length: decodedBytes.count) + } + + deinit { + if let allocatedBytes = _bytes { + _deallocHandler?.handler(allocatedBytes, _length) + } + if type(of: self) === NSData.self || type(of: self) === NSMutableData.self { + _CFDeinit(self._cfObject) + } + } + + // MARK: - Funnel methods open var length: Int { return CFDataGetLength(_cfObject) } - + open var bytes: UnsafeRawPointer { return UnsafeRawPointer(CFDataGetBytePtr(_cfObject)) } + + + + // MARK: - NSObject methods + open override var hash: Int { + return Int(bitPattern: CFHash(_cfObject)) + } + + open override func isEqual(_ object: AnyObject?) -> Bool { + if let data = object as? NSData { + return self.isEqual(to: data._swiftObject) + } else { + return false + } + } + open func isEqual(to other: Data) -> Bool { + if length != other.count { + return false + } + + return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in + let bytes1 = bytes + return memcmp(bytes1, bytes2, length) == 0 + } + } open override func copy() -> Any { return copy(with: nil) @@ -157,42 +254,6 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { open func mutableCopy(with zone: NSZone? = nil) -> Any { return NSMutableData(bytes: UnsafeMutableRawPointer(mutating: bytes), length: length, copy: true, deallocator: nil) } - - open func encode(with aCoder: NSCoder) { - if let aKeyedCoder = aCoder as? NSKeyedArchiver { - aKeyedCoder._encodePropertyList(self, forKey: "NS.data") - } else { - let bytePtr = self.bytes.bindMemory(to: UInt8.self, capacity: self.length) - aCoder.encodeBytes(bytePtr, length: self.length) - } - } - - public required convenience init?(coder aDecoder: NSCoder) { - if !aDecoder.allowsKeyedCoding { - if let data = aDecoder.decodeDataObject() { - self.init(data: data) - } else { - return nil - } - } else if type(of: aDecoder) == NSKeyedUnarchiver.self || aDecoder.containsValue(forKey: "NS.data") { - guard let data = aDecoder._decodePropertyListForKey("NS.data") as? NSData else { - return nil - } - self.init(data: data._swiftObject) - } else { - let result : Data? = aDecoder.withDecodedUnsafeBufferPointer(forKey: "NS.bytes") { - guard let buffer = $0 else { return nil } - return Data(buffer: buffer) - } - - guard let r = result else { return nil } - self.init(data: r) - } - } - - public static var supportsSecureCoding: Bool { - return true - } private func byteDescription(limit: Int? = nil) -> String { var s = "" @@ -226,34 +287,45 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { return "<\(byteDescription())>" } - override open var _cfTypeID: CFTypeID { - return CFDataGetTypeID() - } -} - -extension NSData { - public convenience init(bytes: UnsafeRawPointer?, length: Int) { - self.init(bytes: UnsafeMutableRawPointer(mutating: bytes), length: length, copy: true, deallocator: nil) - } - - public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int) { - self.init(bytes: bytes, length: length, copy: false, deallocator: nil) + // MARK: - NSCoding methods + open func encode(with aCoder: NSCoder) { + if let aKeyedCoder = aCoder as? NSKeyedArchiver { + aKeyedCoder._encodePropertyList(self, forKey: "NS.data") + } else { + let bytePtr = self.bytes.bindMemory(to: UInt8.self, capacity: self.length) + aCoder.encodeBytes(bytePtr, length: self.length) + } } - public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, freeWhenDone b: Bool) { - self.init(bytes: bytes, length: length, copy: false) { buffer, length in - if b { - free(buffer) + public required convenience init?(coder aDecoder: NSCoder) { + if !aDecoder.allowsKeyedCoding { + if let data = aDecoder.decodeDataObject() { + self.init(data: data) + } else { + return nil } + } else if type(of: aDecoder) == NSKeyedUnarchiver.self || aDecoder.containsValue(forKey: "NS.data") { + guard let data = aDecoder._decodePropertyListForKey("NS.data") as? NSData else { + return nil + } + self.init(data: data._swiftObject) + } else { + let result : Data? = aDecoder.withDecodedUnsafeBufferPointer(forKey: "NS.bytes") { + guard let buffer = $0 else { return nil } + return Data(buffer: buffer) + } + + guard let r = result else { return nil } + self.init(data: r) } } - - public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { - self.init(bytes: bytes, length: length, copy: false, deallocator: deallocator) - } - + public static var supportsSecureCoding: Bool { + return true + } + + // MARK: - IO internal struct NSDataReadResult { var bytes: UnsafeMutableRawPointer var length: Int @@ -283,7 +355,7 @@ extension NSData { let length = Int(info.st_size) - if options.contains(.dataReadingMappedAlways) { + if options.contains(.alwaysMapped) { let data = mmap(nil, length, PROT_READ, MAP_PRIVATE, fd, 0) // Swift does not currently expose MAP_FAILURE @@ -316,90 +388,7 @@ extension NSData { } } - public convenience init(contentsOfFile path: String, options readOptionsMask: ReadingOptions) throws { - let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: readOptionsMask) - self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) - } - - public convenience init?(contentsOfFile path: String) { - do { - let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: []) - self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) - } catch { - return nil - } - } - - public convenience init(data: Data) { - self.init(bytes:data._nsObject.bytes, length: data.count) - } - - public convenience init(contentsOf url: URL, options readOptionsMask: ReadingOptions) throws { - if url.isFileURL { - try self.init(contentsOfFile: url.path!, options: readOptionsMask) - } else { - let session = URLSession(configuration: URLSessionConfiguration.defaultSessionConfiguration()) - let cond = NSCondition() - var resError: NSError? - var resData: Data? - let task = session.dataTaskWithURL(url, completionHandler: { (data: Data?, response: URLResponse?, error: NSError?) -> Void in - resData = data - resError = error - cond.broadcast() - }) - task.resume() - cond.wait() - if resData == nil { - throw resError! - } - self.init(data: resData!) - } - } - - public convenience init?(contentsOfURL url: URL) { - do { - try self.init(contentsOf: url, options: []) - } catch { - return nil - } - } -} - -extension NSData { - public func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { - let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: length) - CFDataGetBytes(_cfObject, CFRangeMake(0, length), bytePtr) - } - - public func getBytes(_ buffer: UnsafeMutableRawPointer, range: NSRange) { - let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: range.length) - CFDataGetBytes(_cfObject, CFRangeMake(range.location, range.length), bytePtr) - } - - public func isEqual(to other: Data) -> Bool { - - if length != other.count { - return false - } - - - return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in - let bytes1 = bytes - return memcmp(bytes1, bytes2, length) == 0 - } - } - - public func subdata(with range: NSRange) -> Data { - if range.length == 0 { - return Data() - } - if range.location == 0 && range.length == self.length { - return Data(_bridged: self) - } - return Data(bytes: bytes.advanced(by: range.location), count: range.length) - } - - internal func makeTemporaryFileInDirectory(_ dirPath: String) throws -> (Int32, String) { + internal func makeTemporaryFile(inDirectory dirPath: String) throws -> (Int32, String) { let template = dirPath._nsObject.stringByAppendingPathComponent("tmp.XXXXXX") let maxLength = Int(PATH_MAX) + 1 var buf = [Int8](repeating: 0, count: maxLength) @@ -412,7 +401,7 @@ extension NSData { return (fd, pathResult) } - internal class func writeToFileDescriptor(_ fd: Int32, path: String? = nil, buf: UnsafeRawPointer, length: Int) throws { + internal class func write(toFileDescriptor fd: Int32, path: String? = nil, buf: UnsafeRawPointer, length: Int) throws { var bytesRemaining = length while bytesRemaining > 0 { var bytesWritten : Int @@ -431,10 +420,10 @@ extension NSData { } } - public func write(toFile path: String, options writeOptionsMask: WritingOptions = []) throws { + open func write(toFile path: String, options writeOptionsMask: WritingOptions = []) throws { var fd : Int32 var mode : mode_t? = nil - let useAuxiliaryFile = writeOptionsMask.contains(.dataWritingAtomic) + let useAuxiliaryFile = writeOptionsMask.contains(.atomic) var auxFilePath : String? = nil if useAuxiliaryFile { // Preserve permissions. @@ -444,13 +433,13 @@ extension NSData { } else if errno != ENOENT && errno != ENAMETOOLONG { throw _NSErrorWithErrno(errno, reading: false, path: path) } - let (newFD, path) = try self.makeTemporaryFileInDirectory(path._nsObject.stringByDeletingLastPathComponent) + let (newFD, path) = try self.makeTemporaryFile(inDirectory: path._nsObject.stringByDeletingLastPathComponent) fd = newFD auxFilePath = path fchmod(fd, 0o666) } else { var flags = O_WRONLY | O_CREAT | O_TRUNC - if writeOptionsMask.contains(.dataWritingWithoutOverwriting) { + if writeOptionsMask.contains(.withoutOverwriting) { flags |= O_EXCL } fd = _CFOpenFileWithMode(path, flags, 0o666) @@ -465,7 +454,7 @@ extension NSData { try self.enumerateByteRangesUsingBlockRethrows { (buf, range, stop) in if range.length > 0 { do { - try NSData.writeToFileDescriptor(fd, path: path, buf: buf, length: range.length) + try NSData.write(toFileDescriptor: fd, path: path, buf: buf, length: range.length) if fsync(fd) < 0 { throw _NSErrorWithErrno(errno, reading: false, path: path) } @@ -492,16 +481,18 @@ extension NSData { } } - public func write(toFile path: String, atomically useAuxiliaryFile: Bool) -> Bool { + /// NOTE: the 'atomically' flag is ignored if the url is not of a type the supports atomic writes + open func write(toFile path: String, atomically useAuxiliaryFile: Bool) -> Bool { do { - try write(toFile: path, options: useAuxiliaryFile ? .dataWritingAtomic : []) + try write(toFile: path, options: useAuxiliaryFile ? .atomic : []) } catch { return false } return true } - public func write(to url: URL, atomically: Bool) -> Bool { + /// NOTE: the 'atomically' flag is ignored if the url is not of a type the supports atomic writes + open func write(to url: URL, atomically: Bool) -> Bool { if url.isFileURL { if let path = url.path { return write(toFile: path, atomically: atomically) @@ -518,7 +509,7 @@ extension NSData { /// - throws: This method returns Void and is marked with the `throws` keyword to indicate that it throws an error in the event of failure. /// /// This method is invoked in a `try` expression and the caller is responsible for handling any errors in the `catch` clauses of a `do` statement, as described in [Error Handling](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42) in [The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097) and [Error Handling](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10) in [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216). - public func write(to url: URL, options writeOptionsMask: WritingOptions = []) throws { + open func write(to url: URL, options writeOptionsMask: WritingOptions = []) throws { guard let path = url.path, url.isFileURL == true else { let userInfo = [NSLocalizedDescriptionKey : "The folder at “\(url)” does not exist or is not a file URL.", // NSLocalizedString() not yet available NSURLErrorKey : url.absoluteString ?? ""] as Dictionary @@ -527,8 +518,31 @@ extension NSData { try write(toFile: path, options: writeOptionsMask) } - public func range(of searchData: Data, options mask: SearchOptions = [], in searchRange: NSRange) -> NSRange { - let dataToFind = searchData._nsObject + + // MARK: - Bytes + open func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { + let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: length) + CFDataGetBytes(_cfObject, CFRangeMake(0, length), bytePtr) + } + + open func getBytes(_ buffer: UnsafeMutableRawPointer, range: NSRange) { + let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: range.length) + CFDataGetBytes(_cfObject, CFRangeMake(range.location, range.length), bytePtr) + } + + open func subdata(with range: NSRange) -> Data { + if range.length == 0 { + return Data() + } + if range.location == 0 && range.length == self.length { + return Data(referencing: self) + } + let p = self.bytes.advanced(by: range.location).bindMemory(to: UInt8.self, capacity: range.length) + return Data(bytes: p, count: range.length) + } + + open func range(of dataToFind: Data, options mask: SearchOptions = [], in searchRange: NSRange) -> NSRange { + let dataToFind = dataToFind._nsObject guard dataToFind.length > 0 else {return NSRange(location: NSNotFound, length: 0)} guard let searchRange = searchRange.toRange() else {fatalError("invalid range")} @@ -572,118 +586,37 @@ extension NSData { } } - public func enumerateBytes(_ block: (UnsafeRawPointer, NSRange, UnsafeMutablePointer) -> Void) { + /// 'block' is called once for each contiguous region of memory in the receiver (once total for contiguous NSDatas), until either all bytes have been enumerated, or the 'stop' parameter is set to true. + open func enumerateBytes(_ block: (UnsafeRawPointer, NSRange, UnsafeMutablePointer) -> Void) { var stop = false withUnsafeMutablePointer(to: &stop) { stopPointer in + if (stopPointer.pointee) { + return + } block(bytes, NSMakeRange(0, length), stopPointer) } } -} - -extension NSData : _CFBridgable, _SwiftBridgable { - typealias SwiftType = Data - internal var _swiftObject: SwiftType { return Data(_bridged: self) } - - public func bridge() -> Data { - return _swiftObject - } -} - -extension Data : _NSBridgable, _CFBridgable { - typealias CFType = CFData - typealias NSType = NSData - internal var _cfObject: CFType { return _nsObject._cfObject } - internal var _nsObject: NSType { return _bridgeToObjectiveC() } - - public func bridge() -> NSData { - return _nsObject - } -} - -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) } -} - -extension NSMutableData { - internal var _cfMutableObject: CFMutableData { return unsafeBitCast(self, to: CFMutableData.self) } -} - -open class NSMutableData : NSData { - - public required convenience init() { - self.init(bytes: nil, length: 0) - } - - internal override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { - super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) - } - - open var mutableBytes: UnsafeMutableRawPointer { - return UnsafeMutableRawPointer(CFDataGetMutableBytePtr(_cfMutableObject)) - } - - open override var length: Int { - get { - return CFDataGetLength(_cfObject) - } - set { - CFDataSetLength(_cfMutableObject, newValue) - } - } - open override func copy(with zone: NSZone? = nil) -> Any { - return NSData(bytes: bytes, length: length) - } -} + // MARK: - Base64 Methods -extension NSData { - - /* Create an NSData from a Base-64 encoded NSString using the given options. By default, returns nil when the input is not recognized as valid Base-64. - */ - public convenience init?(base64Encoded base64String: String, options: Base64DecodingOptions) { - let encodedBytes = Array(base64String.utf8) - guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { - return nil - } - self.init(bytes: decodedBytes, length: decodedBytes.count) - } - - /* Create a Base-64 encoded NSString from the receiver's contents using the given options. - */ - public func base64EncodedString(_ options: Base64EncodingOptions = []) -> String { + /// Create a Base-64 encoded String from the receiver's contents using the given options. + open func base64EncodedString(options: Base64EncodingOptions = []) -> String { var decodedBytes = [UInt8](repeating: 0, count: self.length) getBytes(&decodedBytes, length: decodedBytes.count) let encodedBytes = NSData.base64EncodeBytes(decodedBytes, options: options) let characters = encodedBytes.map { Character(UnicodeScalar($0)) } return String(characters) } - - /* Create an NSData from a Base-64, UTF-8 encoded NSData. By default, returns nil when the input is not recognized as valid Base-64. - */ - public convenience init?(base64Encoded base64Data: Data, options: Base64DecodingOptions) { - var encodedBytes = [UInt8](repeating: 0, count: base64Data.count) - base64Data._nsObject.getBytes(&encodedBytes, length: encodedBytes.count) - guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { - return nil - } - self.init(bytes: decodedBytes, length: decodedBytes.count) - } - - /* Create a Base-64, UTF-8 encoded NSData from the receiver's contents using the given options. - */ - public func base64EncodedData(_ options: Base64EncodingOptions = []) -> Data { + + /// Create a Base-64, UTF-8 encoded Data from the receiver's contents using the given options. + open func base64EncodedData(options: Base64EncodingOptions = []) -> Data { var decodedBytes = [UInt8](repeating: 0, count: self.length) getBytes(&decodedBytes, length: decodedBytes.count) let encodedBytes = NSData.base64EncodeBytes(decodedBytes, options: options) return Data(bytes: encodedBytes, count: encodedBytes.count) } - - /** - The ranges of ASCII characters that are used to encode data in Base64. - */ + + /// The ranges of ASCII characters that are used to encode data in Base64. private static let base64ByteMappings: [Range] = [ 65 ..< 91, // A-Z 97 ..< 123, // a-z @@ -697,12 +630,12 @@ extension NSData { private static let base64Padding : UInt8 = 61 // = /** - This method takes a byte with a character from Base64-encoded string - and gets the binary value that the character corresponds to. + This method takes a byte with a character from Base64-encoded string + and gets the binary value that the character corresponds to. - - parameter byte: The byte with the Base64 character. - - returns: Base64DecodedByte value containing the result (Valid , Invalid, Padding) - */ + - parameter byte: The byte with the Base64 character. + - returns: Base64DecodedByte value containing the result (Valid , Invalid, Padding) + */ private enum Base64DecodedByte { case valid(UInt8) case invalid @@ -722,15 +655,15 @@ extension NSData { } /** - This method takes six bits of binary data and encodes it as a character - in Base64. - - The value in the byte must be less than 64, because a Base64 character - can only represent 6 bits. - - - parameter byte: The byte to encode - - returns: The ASCII value for the encoded character. - */ + This method takes six bits of binary data and encodes it as a character + in Base64. + + The value in the byte must be less than 64, because a Base64 character + can only represent 6 bits. + + - parameter byte: The byte to encode + - returns: The ASCII value for the encoded character. + */ private static func base64EncodeByte(_ byte: UInt8) -> UInt8 { assert(byte < 64) var decodedStart: UInt8 = 0 @@ -746,19 +679,19 @@ extension NSData { /** - This method decodes Base64-encoded data. + This method decodes Base64-encoded data. - If the input contains any bytes that are not valid Base64 characters, - this will return nil. - - - parameter bytes: The Base64 bytes - - parameter options: Options for handling invalid input - - returns: The decoded bytes. - */ + If the input contains any bytes that are not valid Base64 characters, + this will return nil. + + - parameter bytes: The Base64 bytes + - parameter options: Options for handling invalid input + - returns: The decoded bytes. + */ private static func base64DecodeBytes(_ bytes: [UInt8], options: Base64DecodingOptions = []) -> [UInt8]? { var decodedBytes = [UInt8]() decodedBytes.reserveCapacity((bytes.count/3)*2) - + var currentByte : UInt8 = 0 var validCharacterCount = 0 var paddingCount = 0 @@ -819,12 +752,12 @@ extension NSData { /** - This method encodes data in Base64. + This method encodes data in Base64. - - parameter bytes: The bytes you want to encode - - parameter options: Options for formatting the result - - returns: The Base64-encoding for those bytes. - */ + - parameter bytes: The bytes you want to encode + - parameter options: Options for formatting the result + - returns: The Base64-encoding for those bytes. + */ private static func base64EncodeBytes(_ bytes: [UInt8], options: Base64EncodingOptions = []) -> [UInt8] { var result = [UInt8]() result.reserveCapacity((bytes.count/3)*4) @@ -832,15 +765,15 @@ extension NSData { let lineOptions : (lineLength : Int, separator : [UInt8])? = { let lineLength: Int - if options.contains(.encoding64CharacterLineLength) { lineLength = 64 } - else if options.contains(.encoding76CharacterLineLength) { lineLength = 76 } + if options.contains(.lineLength64Characters) { lineLength = 64 } + else if options.contains(.lineLength76Characters) { lineLength = 76 } else { return nil } var separator = [UInt8]() - if options.contains(.encodingEndLineWithCarriageReturn) { separator.append(13) } - if options.contains(.encodingEndLineWithLineFeed) { separator.append(10) } + if options.contains(.endLineWithCarriageReturn) { separator.append(13) } + if options.contains(.endLineWithLineFeed) { separator.append(10) } //if the kind of line ending to insert is not specified, the default line ending is Carriage Return + Line Feed. if separator.count == 0 {separator = [13,10]} @@ -894,16 +827,83 @@ extension NSData { } return result } + +} + +// MARK: - +extension NSData : _CFBridgable, _SwiftBridgable { + typealias SwiftType = Data + internal var _swiftObject: SwiftType { return Data(referencing: self) } + + public func bridge() -> Data { + return _swiftObject + } +} + +extension Data : _NSBridgable, _CFBridgable { + typealias CFType = CFData + typealias NSType = NSData + internal var _cfObject: CFType { return _nsObject._cfObject } + internal var _nsObject: NSType { return _bridgeToObjectiveC() } + + public func bridge() -> NSData { + return _nsObject + } } -extension NSMutableData { +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(referencing: self._nsObject) } +} - public func append(_ bytes: UnsafeRawPointer, length: Int) { +// MARK: - +open class NSMutableData : NSData { + internal var _cfMutableObject: CFMutableData { return unsafeBitCast(self, to: CFMutableData.self) } + + public override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { + super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) + } + public init() { + self.init(bytes: nil, length: 0) + } + + public convenience init?(capacity: Int) { + self.init(bytes: nil, length: 0) + } + + public convenience init?(length: Int) { + self.init(bytes: nil, length: 0) + self.length = length + } + + // MARK: - Funnel Methods + open var mutableBytes: UnsafeMutableRawPointer { + return UnsafeMutableRawPointer(CFDataGetMutableBytePtr(_cfMutableObject)) + } + + open override var length: Int { + get { + return CFDataGetLength(_cfObject) + } + set { + CFDataSetLength(_cfMutableObject, newValue) + } + } + + // MARK: - NSObject + open override func copy(with zone: NSZone? = nil) -> Any { + return NSData(bytes: bytes, length: length) + } + + // MARK: - Mutability + open func append(_ bytes: UnsafeRawPointer, length: Int) { let bytePtr = bytes.bindMemory(to: UInt8.self, capacity: length) CFDataAppendBytes(_cfMutableObject, bytePtr, length) } - public func append(_ other: Data) { + open func append(_ other: Data) { let otherLength = other.count other.withUnsafeBytes { append($0, length: otherLength) @@ -911,20 +911,20 @@ extension NSMutableData { } - public func increaseLength(by extraLength: Int) { + open func increaseLength(by extraLength: Int) { CFDataSetLength(_cfMutableObject, CFDataGetLength(_cfObject) + extraLength) } - public func replaceBytes(in range: NSRange, withBytes bytes: UnsafeRawPointer) { + open func replaceBytes(in range: NSRange, withBytes bytes: UnsafeRawPointer) { let bytePtr = bytes.bindMemory(to: UInt8.self, capacity: length) CFDataReplaceBytes(_cfMutableObject, CFRangeMake(range.location, range.length), bytePtr, length) } - public func resetBytes(in range: NSRange) { + open func resetBytes(in range: NSRange) { bzero(mutableBytes.advanced(by: range.location), range.length) } - public func setData(_ data: Data) { + open func setData(_ data: Data) { length = data.count data.withUnsafeBytes { replaceBytes(in: NSMakeRange(0, length), withBytes: $0) @@ -932,20 +932,10 @@ extension NSMutableData { } - public func replaceBytes(in range: NSRange, withBytes replacementBytes: UnsafeRawPointer, length replacementLength: Int) { - let bytePtr = replacementBytes.bindMemory(to: UInt8.self, capacity: replacementLength) - CFDataReplaceBytes(_cfMutableObject, CFRangeMake(range.location, range.length), bytePtr, replacementLength) - } -} - -extension NSMutableData { - - public convenience init?(capacity: Int) { - self.init(bytes: nil, length: 0) - } - - public convenience init?(length: Int) { - self.init(bytes: nil, length: 0) - self.length = length + open func replaceBytes(in range: NSRange, withBytes replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { + if let replacementBytes = replacementBytes { + let bytePtr = replacementBytes.bindMemory(to: UInt8.self, capacity: replacementLength) + CFDataReplaceBytes(_cfMutableObject, CFRangeMake(range.location, range.length), bytePtr, replacementLength) + } } } diff --git a/Foundation/NSFileHandle.swift b/Foundation/NSFileHandle.swift index 6812fecede..df2e935b8e 100644 --- a/Foundation/NSFileHandle.swift +++ b/Foundation/NSFileHandle.swift @@ -118,7 +118,7 @@ open class FileHandle: NSObject, NSSecureCoding { open func write(_ data: Data) { data.enumerateBytes() { (bytes, range, stop) in do { - try NSData.writeToFileDescriptor(self._fd, path: nil, buf: UnsafeRawPointer(bytes.baseAddress!), length: bytes.count) + try NSData.write(toFileDescriptor: self._fd, path: nil, buf: UnsafeRawPointer(bytes.baseAddress!), length: bytes.count) } catch { fatalError("Write failure") } diff --git a/Foundation/NSFileManager.swift b/Foundation/NSFileManager.swift index 8adc8ecf97..92b192e117 100644 --- a/Foundation/NSFileManager.swift +++ b/Foundation/NSFileManager.swift @@ -686,7 +686,7 @@ open class FileManager: NSObject { open func createFile(atPath path: String, contents data: Data?, attributes attr: [String : Any]? = [:]) -> Bool { do { - try (data ?? Data()).write(to: URL(fileURLWithPath: path), options: .dataWritingAtomic) + try (data ?? Data()).write(to: URL(fileURLWithPath: path), options: .atomic) return true } catch _ { return false diff --git a/Foundation/NSString.swift b/Foundation/NSString.swift index b5f18fa6a8..ae193f571f 100644 --- a/Foundation/NSString.swift +++ b/Foundation/NSString.swift @@ -831,7 +831,7 @@ extension NSString { return String.Encoding.unicode.rawValue } - public func data(using encoding: UInt, allowLossyConversion lossy: Bool) -> Data? { + public func data(using encoding: UInt, allowLossyConversion lossy: Bool = false) -> Data? { let len = length var reqSize = 0 @@ -844,7 +844,7 @@ extension NSString { if convertedLen != len { return nil // Not able to do it all... } - + if 0 < reqSize { var data = Data(count: reqSize) data.count = data.withUnsafeMutableBytes { (mutableBytes: UnsafeMutablePointer) -> Int in @@ -1160,7 +1160,7 @@ extension NSString { internal func _writeTo(_ url: URL, _ useAuxiliaryFile: Bool, _ enc: UInt) throws { var data = Data() try _getExternalRepresentation(&data, url, enc) - try data.write(to: url, options: useAuxiliaryFile ? .dataWritingAtomic : []) + try data.write(to: url, options: useAuxiliaryFile ? .atomic : []) } open func write(to url: URL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws { diff --git a/Foundation/NSXMLDocument.swift b/Foundation/NSXMLDocument.swift index f7b378907c..ca82b6f065 100644 --- a/Foundation/NSXMLDocument.swift +++ b/Foundation/NSXMLDocument.swift @@ -82,7 +82,7 @@ open class XMLDocument : XMLNode { @abstract Returns a document created from the contents of an XML or HTML URL. Connection problems such as 404, parse errors are returned in error. */ public convenience init(contentsOf url: URL, options: Options) throws { - let data = try Data(contentsOf: url, options: .dataReadingMappedIfSafe) + let data = try Data(contentsOf: url, options: .mappedIfSafe) try self.init(data: data, options: options) } diff --git a/Foundation/NSXMLParser.swift b/Foundation/NSXMLParser.swift index d6bed25d4e..6ab8595bdb 100644 --- a/Foundation/NSXMLParser.swift +++ b/Foundation/NSXMLParser.swift @@ -381,7 +381,7 @@ internal func _NSXMLParserProcessingInstruction(_ ctx: _CFXMLInterface, target: internal func _NSXMLParserCdataBlock(_ ctx: _CFXMLInterface, value: UnsafePointer, len: Int32) -> Void { let parser = ctx.parser if let delegate = parser.delegate { - delegate.parser(parser, foundCDATA: Data(bytes: UnsafeRawPointer(value), count: Int(len))) + delegate.parser(parser, foundCDATA: Data(bytes: value, count: Int(len))) } } @@ -551,9 +551,9 @@ open class XMLParser : NSObject { _bomChunk = nil if (totalLength > 4) { - let remainingData = allExistingData.withUnsafeBytes { (bytes: UnsafePointer) -> Data in + let remainingData = allExistingData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Data in let ptr = bytes.advanced(by: 4) - return Data(bytesNoCopy: UnsafeMutablePointer(mutating: ptr), count: totalLength - 4, deallocator: .none) + return Data(bytesNoCopy: ptr, count: totalLength - 4, deallocator: .none) } let _ = parseData(remainingData) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 7d6c39867d..71e7b2e5b4 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -17,8 +17,157 @@ class TestNSData: XCTestCase { + // This is a type of Data that starts off as a storage of all 0x01s, but it only creates that buffer when needed. When mutated it converts into a more traditional data storage backed by a buffer. + class AllOnesData : NSMutableData { + + private var _length : Int = 0 + var _pointer : UnsafeMutableBufferPointer? = nil { + willSet { + if let p = _pointer { free(p.baseAddress) } + } + } + public override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { + _length = length + _pointer = nil + super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) + } + + + public override init() { + _length = 0 + _pointer = nil + super.init() + } + convenience init?(length : Int) { + self.init() + _length = length + _pointer = nil + } + + required init?(coder aDecoder: NSCoder) { + // Not tested + fatalError() + } + + deinit { + if let p = _pointer { + free(p.baseAddress) + } + } + + override var length : Int { + get { + return _length + } + set { + if let ptr = _pointer { + // Copy the data to our new length buffer + let newBuffer = malloc(newValue)! + if newValue <= _length { + memmove(newBuffer, ptr.baseAddress, newValue) + } else if newValue > _length { + memmove(newBuffer, ptr.baseAddress, _length) + memset(newBuffer + _length, 1, newValue - _length) + } + let bytePtr = newBuffer.bindMemory(to: UInt8.self, capacity: newValue) + _pointer = UnsafeMutableBufferPointer(start: bytePtr, count: newValue) + } else { + _length = newValue + } + } + } + + override var bytes : UnsafeRawPointer { + if let d = _pointer { + return UnsafeRawPointer(d.baseAddress!) + } else { + // Need to allocate the buffer now. + // It doesn't matter if the buffer is uniquely referenced or not here. + let buffer = malloc(length) + memset(buffer, 1, length) + let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) + let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) + _pointer = result + return UnsafeRawPointer(result.baseAddress!) + } + } + + override var mutableBytes: UnsafeMutableRawPointer { + let newBufferLength = _length + let newBuffer = malloc(newBufferLength) + if let ptr = _pointer { + // Copy the existing data to the new box, then return its pointer + memmove(newBuffer, ptr.baseAddress, newBufferLength) + } else { + // Set new data to 1s + memset(newBuffer, 1, newBufferLength) + } + let bytePtr = newBuffer!.bindMemory(to: UInt8.self, capacity: newBufferLength) + let result = UnsafeMutableBufferPointer(start: bytePtr, count: newBufferLength) + _pointer = result + _length = newBufferLength + return UnsafeMutableRawPointer(result.baseAddress!) + } + + override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { + if let d = _pointer { + // Get the real data from the buffer + memmove(buffer, d.baseAddress, length) + } else { + // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes + memset(buffer, 1, length) + } + } + } + + // MARK: - + + // String of course has its own way to get data, but this way tests our own data struct + func dataFrom(_ string : String) -> Data { + // Create a Data out of those bytes + return string.utf8CString.withUnsafeBufferPointer { (ptr) in + ptr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: ptr.count) { + // Subtract 1 so we don't get the null terminator byte. This matches NSString behavior. + return Data(bytes: $0, count: ptr.count - 1) + } + } + } + static var allTests: [(String, (TestNSData) -> () throws -> Void)] { return [ + ("testBasicConstruction", testBasicConstruction), + ("test_base64Data_medium", test_base64Data_medium), + ("test_base64Data_small", test_base64Data_small), + ("test_basicReadWrite", test_basicReadWrite), + ("test_bufferSizeCalculation", test_bufferSizeCalculation), + // ("test_dataHash", test_dataHash), Disabled due to lack of brdiging in swift runtime -- infinite loops + ("test_genericBuffers", test_genericBuffers), + ("test_writeFailure", test_writeFailure), + ("testBasicConstruction", testBasicConstruction), + ("testBridgingCustom", testBridgingCustom), + ("testBridgingDefault", testBridgingDefault), + ("testBridgingMutable", testBridgingMutable), + ("testCopyBytes_oversized", testCopyBytes_oversized), + ("testCopyBytes_ranges", testCopyBytes_ranges), + ("testCopyBytes_undersized", testCopyBytes_undersized), + // ("testCopyBytes", testCopyBytes), Disabled to due failure, we want this passing - but API matching is more important right now + ("testCustomData", testCustomData), + ("testCustomDeallocator", testCustomDeallocator), + ("testDataInSet", testDataInSet), + ("testEquality", testEquality), + ("testGenericAlgorithms", testGenericAlgorithms), + ("testInitializationWithArray", testInitializationWithArray), + ("testInsertData", testInsertData), + ("testLoops", testLoops), + ("testMutableData", testMutableData), + ("testRange", testRange), + ("testReplaceSubrange", testReplaceSubrange), + ("testReplaceSubrange2", testReplaceSubrange2), + ("testReplaceSubrange3", testReplaceSubrange3), + ("testReplaceSubrange4", testReplaceSubrange4), + ("testReplaceSubrange5", testReplaceSubrange5), + + ("test_description", test_description), ("test_emptyDescription", test_emptyDescription), ("test_longDescription", test_longDescription), @@ -51,7 +200,7 @@ class TestNSData: XCTestCase { let saveData = try! Data(contentsOf: Bundle.main.url(forResource: "Test", withExtension: "plist")!) let savePath = URL(fileURLWithPath: "/var/tmp/Test.plist") do { - try saveData.write(to: savePath, options: .dataWritingAtomic) + try saveData.write(to: savePath, options: .atomic) let fileManager = FileManager.default XCTAssertTrue(fileManager.fileExists(atPath: savePath.path!)) try! fileManager.removeItem(atPath: savePath.path!) @@ -198,7 +347,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData([]) + let encodedData = data.base64EncodedData() guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -213,7 +362,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData([.encoding64CharacterLineLength, .encodingEndLineWithLineFeed]) + let encodedData = data.base64EncodedData(options: [.lineLength64Characters, .endLineWithLineFeed]) guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -228,7 +377,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData([.encoding76CharacterLineLength, .encodingEndLineWithCarriageReturn]) + let encodedData = data.base64EncodedData(options: [.lineLength76Characters, .endLineWithCarriageReturn]) guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -243,7 +392,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData([.encoding76CharacterLineLength, .encodingEndLineWithCarriageReturn, .encodingEndLineWithLineFeed]) + let encodedData = data.base64EncodedData(options: [.lineLength76Characters, .endLineWithCarriageReturn, .endLineWithLineFeed]) guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -258,7 +407,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedTextResult = data.base64EncodedString([]) + let encodedTextResult = data.base64EncodedString() XCTAssertEqual(encodedTextResult, encodedText) } @@ -352,9 +501,9 @@ class TestNSData: XCTestCase { var data = Data(bytes: [0, 0, 0, 0, 0]) let newData = Data(bytes: [1, 2, 3, 4, 5]) - // test Data.replaceBytes(in:with:) + // test replaceSubrange(_, with:) XCTAssertFalse(data == newData) - data.replaceBytes(in: data.startIndex..= 2 }) + XCTAssertEqual(2, data2.count) + + let data3 = Data(bytes: [1, 2, 3, 4, 5][1..<3]) + XCTAssertEqual(2, data3.count) + } + + func testMutableData() { + let hello = dataFrom("hello") + let helloLength = hello.count + XCTAssertEqual(hello[0], 0x68, "Unexpected first byte") + + // Double the length + var mutatingHello = hello + mutatingHello.count *= 2 + + XCTAssertEqual(hello.count, helloLength, "The length of the initial data should not have changed") + XCTAssertEqual(mutatingHello.count, helloLength * 2, "The length should have changed") + + // Get the underlying data for hello2 + mutatingHello.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in + XCTAssertEqual(bytes.pointee, 0x68, "First byte should be 0x68") + + // Mutate it + bytes.pointee = 0x67 + XCTAssertEqual(bytes.pointee, 0x67, "First byte should be 0x67") + XCTAssertEqual(mutatingHello[0], 0x67, "First byte accessed via other method should still be 0x67") + + // Verify that the first data is still correct + XCTAssertEqual(hello[0], 0x68, "The first byte should still be 0x68") + } + } + + func testCustomData() { + let length = 5 + let allOnesData = Data(referencing: AllOnesData(length: length)!) + XCTAssertEqual(1, allOnesData[0], "First byte of all 1s data should be 1") + + // Double the length + var allOnesCopyToMutate = allOnesData + allOnesCopyToMutate.count = allOnesData.count * 2 + + XCTAssertEqual(allOnesData.count, length, "The length of the initial data should not have changed") + XCTAssertEqual(allOnesCopyToMutate.count, length * 2, "The length should have changed") + + // Force the second data to create its storage + allOnesCopyToMutate.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in + XCTAssertEqual(bytes.pointee, 1, "First byte should be 1") + + // Mutate the second data + bytes.pointee = 0 + XCTAssertEqual(bytes.pointee, 0, "First byte should be 0") + XCTAssertEqual(allOnesCopyToMutate[0], 0, "First byte accessed via other method should still be 0") + + // Verify that the first data is still 1 + XCTAssertEqual(allOnesData[0], 1, "The first byte should still be 1") + } + + } + + func testBridgingDefault() { + let hello = dataFrom("hello") + // Convert from struct Data to NSData + if let s = NSString(data: hello, encoding: String.Encoding.utf8.rawValue) { + XCTAssertTrue(s.isEqual(to: "hello"), "The strings should be equal") + } + + // Convert from NSData to struct Data + let goodbye = dataFrom("goodbye") + if let resultingData = NSString(string: "goodbye").data(using: String.Encoding.utf8.rawValue) { + XCTAssertEqual(resultingData[0], goodbye[0], "First byte should be equal") + } + } + + func testBridgingMutable() { + // Create a mutable data + var helloWorld = dataFrom("hello") + helloWorld.append(dataFrom("world")) + + // Convert from struct Data to NSData + if let s = NSString(data: helloWorld, encoding: String.Encoding.utf8.rawValue) { + XCTAssertTrue(s.isEqual(to: "helloworld"), "The strings should be equal") + } + + } + + func testBridgingCustom() { + // Let's use an AllOnesData with some Objective-C code + let allOnes = AllOnesData(length: 64)! + + // Type-erased + let data = Data(referencing: allOnes) + + // Create a home for our test data + let x = NSString() + let dirPath = try! (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) + try! FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) + let filePath = (dirPath.bridge()).stringByAppendingPathComponent("temp_file") + guard FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil) else { XCTAssertTrue(false, "Unable to create temporary file"); return} + guard let fh = FileHandle(forWritingAtPath: filePath) else { XCTAssertTrue(false, "Unable to open temporary file"); return } + defer { try! FileManager.default.removeItem(atPath: dirPath) } + + // Now use this data with some Objective-C code that takes NSData arguments + fh.write(data) + + // Get the data back + do { + let url = URL(fileURLWithPath: filePath) + let readData = try Data.init(contentsOf: url) + XCTAssertEqual(data.count, readData.count, "The length of the data is not the same") + } catch { + XCTAssertTrue(false, "Unable to read back data") + return + } + } + + func testEquality() { + let d1 = dataFrom("hello") + let d2 = dataFrom("hello") + + // Use == explicitly here to make sure we're calling the right methods + XCTAssertTrue(d1 == d2, "Data should be equal") + } + + func testDataInSet() { + let d1 = dataFrom("Hello") + let d2 = dataFrom("Hello") + let d3 = dataFrom("World") + + var s = Set() + s.insert(d1) + s.insert(d2) + s.insert(d3) + + XCTAssertEqual(s.count, 2, "Expected only two entries in the Set") + } + + func testReplaceSubrange() { + var hello = dataFrom("Hello") + let world = dataFrom("World") + + hello[0] = world[0] + XCTAssertEqual(hello[0], world[0]) + + var goodbyeWorld = dataFrom("Hello World") + let goodbye = dataFrom("Goodbye") + let expected = dataFrom("Goodbye World") + + goodbyeWorld.replaceSubrange(0..<5, with: goodbye) + XCTAssertEqual(goodbyeWorld, expected) + } + + func testReplaceSubrange2() { + let hello = dataFrom("Hello") + let world = dataFrom(" World") + let goodbye = dataFrom("Goodbye") + let expected = dataFrom("Goodbye World") + + var mutateMe = hello + mutateMe.append(world) + + if let found = mutateMe.range(of: hello) { + mutateMe.replaceSubrange(found, with: goodbye) + } + XCTAssertEqual(mutateMe, expected) + } + + func testReplaceSubrange3() { + // The expected result + let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] + let expected = expectedBytes.withUnsafeBufferPointer { + return Data(buffer: $0) + } + + // The data we'll mutate + let someBytes : [UInt8] = [1, 2, 3, 4, 5] + var a = someBytes.withUnsafeBufferPointer { + return Data(buffer: $0) + } + + // The bytes we'll insert + let b : [UInt8] = [9, 10, 11, 12, 13] + b.withUnsafeBufferPointer { + a.replaceSubrange(2..<5, with: $0) + } + XCTAssertEqual(expected, a) + } + + func testReplaceSubrange4() { + let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] + let expected = Data(bytes: expectedBytes) + + // The data we'll mutate + let someBytes : [UInt8] = [1, 2, 3, 4, 5] + var a = Data(bytes: someBytes) + + // The bytes we'll insert + let b : [UInt8] = [9, 10, 11, 12, 13] + a.replaceSubrange(2..<5, with: b) + XCTAssertEqual(expected, a) + } + + func testReplaceSubrange5() { + var d = Data(bytes: [1, 2, 3]) + d.replaceSubrange(0..<0, with: [4]) + XCTAssertEqual(Data(bytes: [4, 1, 2, 3]), d) + + d.replaceSubrange(0..<4, with: [9]) + XCTAssertEqual(Data(bytes: [9]), d) + + d.replaceSubrange(0..= 65 && byte <= 90 } + + let allCaps = hello.filter(isCapital) + XCTAssertEqual(allCaps.count, 2) + + let capCount = hello.reduce(0) { isCapital($1) ? $0 + 1 : $0 } + XCTAssertEqual(capCount, 2) + + let allLower = hello.map { isCapital($0) ? $0 + 31 : $0 } + XCTAssertEqual(allLower.count, hello.count) + } + + func testCustomDeallocator() { + var deallocatorCalled = false + + // Scope the data to a block to control lifecycle + do { + let buffer = malloc(16)! + let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: 16) + var data = Data(bytesNoCopy: bytePtr, count: 16, deallocator: .custom({ (ptr, size) in + deallocatorCalled = true + free(UnsafeMutableRawPointer(ptr)) + })) + // Use the data + data[0] = 1 + } + + XCTAssertTrue(deallocatorCalled, "Custom deallocator was never called") + } + + func testCopyBytes() { + let c = 10 + let underlyingBuffer = malloc(c * MemoryLayout.stride)! + let u16Ptr = underlyingBuffer.bindMemory(to: UInt16.self, capacity: c) + let buffer = UnsafeMutableBufferPointer(start: u16Ptr, count: c) + + buffer[0] = 0 + buffer[1] = 0 + + var data = Data(capacity: c * MemoryLayout.stride) + data.resetBytes(in: 0...stride) + data[0] = 0xFF + data[1] = 0xFF + let copiedCount = data.copyBytes(to: buffer) + XCTAssertEqual(copiedCount, c * MemoryLayout.stride) + + XCTAssertEqual(buffer[0], 0xFFFF) + free(underlyingBuffer) + } + + func testCopyBytes_undersized() { + let a : [UInt8] = [1, 2, 3, 4, 5] + var data = a.withUnsafeBufferPointer { + return Data(buffer: $0) + } + let expectedSize = MemoryLayout.stride * a.count + XCTAssertEqual(expectedSize, data.count) + + let underlyingBuffer = unsafeBitCast(malloc(expectedSize - 1)!, to: UnsafeMutablePointer.self) + let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize - 1) + + // We should only copy in enough bytes that can fit in the buffer + let copiedCount = data.copyBytes(to: buffer) + XCTAssertEqual(expectedSize - 1, copiedCount) + + var index = 0 + for v in a[0...stride * a.count + XCTAssertEqual(expectedSize, data.count) + + let underlyingBuffer = unsafeBitCast(malloc(expectedSize + 1)!, to: UnsafeMutablePointer.self) + let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize + 1) + + let copiedCount = data.copyBytes(to: buffer) + XCTAssertEqual(expectedSize, copiedCount) + + free(underlyingBuffer) + } + + func testCopyBytes_ranges() { + + do { + // Equal sized buffer, data + let a : [UInt8] = [1, 2, 3, 4, 5] + var data = a.withUnsafeBufferPointer { + return Data(buffer: $0) + } + + let underlyingBuffer = unsafeBitCast(malloc(data.count)!, to: UnsafeMutablePointer.self) + let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: data.count) + + var copiedCount : Int + + copiedCount = data.copyBytes(to: buffer, from: 0..<0) + XCTAssertEqual(0, copiedCount) + + copiedCount = data.copyBytes(to: buffer, from: 1..<1) + XCTAssertEqual(0, copiedCount) + + copiedCount = data.copyBytes(to: buffer, from: 0..<3) + XCTAssertEqual((0..<3).count, copiedCount) + + var index = 0 + for v in a[0..<3] { + XCTAssertEqual(v, buffer[index]) + index += 1 + } + free(underlyingBuffer) + } + + do { + // Larger buffer than data + let a : [UInt8] = [1, 2, 3, 4] + let data = a.withUnsafeBufferPointer { + return Data(buffer: $0) + } + + let underlyingBuffer = unsafeBitCast(malloc(10)!, to: UnsafeMutablePointer.self) + let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: 10) + + var copiedCount : Int + + copiedCount = data.copyBytes(to: buffer, from: 0..<3) + XCTAssertEqual((0..<3).count, copiedCount) + + var index = 0 + for v in a[0..<3] { + XCTAssertEqual(v, buffer[index]) + index += 1 + } + free(underlyingBuffer) + } + + do { + // Larger data than buffer + let a : [UInt8] = [1, 2, 3, 4, 5, 6] + let data = a.withUnsafeBufferPointer { + return Data(buffer: $0) + } + + let underlyingBuffer = unsafeBitCast(malloc(4)!, to: UnsafeMutablePointer.self) + let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: 4) + + var copiedCount : Int + + copiedCount = data.copyBytes(to: buffer, from: 0...stride * a.count + XCTAssertEqual(expectedSize, data.count) + + [false, true].withUnsafeBufferPointer { + data.append($0) + } + + expectedSize += MemoryLayout.stride * 2 + XCTAssertEqual(expectedSize, data.count) + + let underlyingBuffer = unsafeBitCast(malloc(expectedSize)!, to: UnsafeMutablePointer.self) + + let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize) + let copiedCount = data.copyBytes(to: buffer) + XCTAssertEqual(copiedCount, expectedSize) + + free(underlyingBuffer) + } + + // intentionally structured so sizeof() != strideof() + struct MyStruct { + var time: UInt64 + let x: UInt32 + let y: UInt32 + let z: UInt32 + init() { + time = 0 + x = 1 + y = 2 + z = 3 + } + } + + func test_bufferSizeCalculation() { + // Make sure that Data is correctly using strideof instead of sizeof. + // n.b. if sizeof(MyStruct) == strideof(MyStruct), this test is not as useful as it could be + + // init + let stuff = [MyStruct(), MyStruct(), MyStruct()] + var data = stuff.withUnsafeBufferPointer { + return Data(buffer: $0) + } + + XCTAssertEqual(data.count, MemoryLayout.stride * 3) + + + // append + stuff.withUnsafeBufferPointer { + data.append($0) + } + + XCTAssertEqual(data.count, MemoryLayout.stride * 6) + + // copyBytes + do { + // equal size + let underlyingBuffer = malloc(6 * MemoryLayout.stride)! + defer { free(underlyingBuffer) } + + let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 6) + let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) + + let byteCount = data.copyBytes(to: buffer) + XCTAssertEqual(6 * MemoryLayout.stride, byteCount) + } + + do { + // undersized + let underlyingBuffer = malloc(3 * MemoryLayout.stride)! + defer { free(underlyingBuffer) } + + let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 3) + let buffer = UnsafeMutableBufferPointer(start: ptr, count: 3) + + let byteCount = data.copyBytes(to: buffer) + XCTAssertEqual(3 * MemoryLayout.stride, byteCount) + } + + do { + // oversized + let underlyingBuffer = malloc(12 * MemoryLayout.stride)! + defer { free(underlyingBuffer) } + + let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 6) + let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) + + let byteCount = data.copyBytes(to: buffer) + XCTAssertEqual(6 * MemoryLayout.stride, byteCount) + } + } +} + diff --git a/TestFoundation/TestNSJSONSerialization.swift b/TestFoundation/TestNSJSONSerialization.swift index 8950515c2e..eca770796a 100644 --- a/TestFoundation/TestNSJSONSerialization.swift +++ b/TestFoundation/TestNSJSONSerialization.swift @@ -45,7 +45,10 @@ extension TestNSJSONSerialization { } func test_JSONObjectWithData_emptyObject() { - let subject = Data(bytes: UnsafeRawPointer([UInt8]([0x7B, 0x7D])), count: 2) + var bytes: [UInt8] = [0x7B, 0x7D] + let subject = bytes.withUnsafeMutableBufferPointer { + return Data(buffer: $0) + } let object = try! JSONSerialization.jsonObject(with: subject, options: []) as? [String:Any] XCTAssertEqual(object?.count, 0) @@ -75,7 +78,7 @@ extension TestNSJSONSerialization { ] for (description, encoded) in subjects { - let result = try? JSONSerialization.jsonObject(with: Data(bytes:UnsafeRawPointer(encoded), count: encoded.count), options: []) + let result = try? JSONSerialization.jsonObject(with: Data(bytes:encoded, count: encoded.count), options: []) XCTAssertNotNil(result, description) } } diff --git a/TestFoundation/TestNSXMLParser.swift b/TestFoundation/TestNSXMLParser.swift index 239d27c100..486f23dd92 100644 --- a/TestFoundation/TestNSXMLParser.swift +++ b/TestFoundation/TestNSXMLParser.swift @@ -28,7 +28,9 @@ class TestNSXMLParser : XCTestCase { func test_data() { let xml = Array("bar".utf8CString) let data = xml.withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> Data in - return Data(bytes: UnsafeRawPointer(buffer.baseAddress!), count: buffer.count) + return buffer.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: buffer.count * MemoryLayout.stride) { + return Data(bytes: $0, count: buffer.count) + } } let parser = XMLParser(data: data) let res = parser.parse() From a365e75cd8ebb77b47071b6bca6ff18f71664588 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Tue, 16 Aug 2016 18:18:15 -0700 Subject: [PATCH 02/12] Restore a few raw pointers uncovered by building on linux --- Foundation/Data.swift | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/Foundation/Data.swift b/Foundation/Data.swift index 17e54aee3e..46579d5f6a 100644 --- a/Foundation/Data.swift +++ b/Foundation/Data.swift @@ -286,12 +286,8 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - private func _getUnsafeBytesPointer() -> UnsafePointer { - - return _mapUnmanaged { - let boundBytes = $0.bytes.bindMemory(to: Void.self, capacity: $0.length) - return boundBytes - } + private func _getUnsafeBytesPointer() -> UnsafeRawPointer { + return _mapUnmanaged { return $0.bytes } } /// Access the bytes in the data. @@ -300,15 +296,13 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public func withUnsafeBytes(_ body: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { let bytes = _getUnsafeBytesPointer() defer { _fixLifetime(self)} - let result = try bytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { - return try body($0) - } - return result + let contentPtr = bytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) + return try body(contentPtr) } - private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutablePointer { + private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutableRawPointer { return _applyUnmanagedMutation { - return $0.mutableBytes.bindMemory(to: Void.self, capacity: count) + return $0.mutableBytes } } @@ -319,11 +313,8 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { let mutableBytes = _getUnsafeMutableBytesPointer() defer { _fixLifetime(self)} - - let result = try mutableBytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { - return try body($0) - } - return result + let contentPtr = mutableBytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) + return try body(contentPtr) } // MARK: - From 7980e6ceb5df7373a3a3be9c4bf5ee13f4fe0fae Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Tue, 16 Aug 2016 18:21:52 -0700 Subject: [PATCH 03/12] Further linux build refinements --- Foundation/Data.swift | 2 +- Foundation/NSData.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Foundation/Data.swift b/Foundation/Data.swift index 46579d5f6a..3f7823c349 100644 --- a/Foundation/Data.swift +++ b/Foundation/Data.swift @@ -270,7 +270,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// - parameter reference: The instance of `NSData` that you wish to wrap. This instance will be copied by `struct Data`. public init(referencing reference: NSData) { - _wrapped = _SwiftNSData(immutableObject: reference.copy() as AnyObject) + _wrapped = _SwiftNSData(immutableObject: reference.copy() as! AnyObject) } // ----------------------------------- diff --git a/Foundation/NSData.swift b/Foundation/NSData.swift index 5c55696478..17efa99862 100644 --- a/Foundation/NSData.swift +++ b/Foundation/NSData.swift @@ -233,7 +233,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { return false } - return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in + return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in let bytes1 = bytes return memcmp(bytes1, bytes2, length) == 0 } From 1b50c1662e42895a4459693a737cdc1b014b0d8f Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Tue, 16 Aug 2016 18:30:46 -0700 Subject: [PATCH 04/12] And more linux fixes - this time for unit tests --- TestFoundation/TestNSData.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 71e7b2e5b4..81cf836952 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -64,9 +64,9 @@ class TestNSData: XCTestCase { // Copy the data to our new length buffer let newBuffer = malloc(newValue)! if newValue <= _length { - memmove(newBuffer, ptr.baseAddress, newValue) + memmove(newBuffer, ptr.baseAddress!, newValue) } else if newValue > _length { - memmove(newBuffer, ptr.baseAddress, _length) + memmove(newBuffer, ptr.baseAddress!, _length) memset(newBuffer + _length, 1, newValue - _length) } let bytePtr = newBuffer.bindMemory(to: UInt8.self, capacity: newValue) @@ -84,7 +84,7 @@ class TestNSData: XCTestCase { // Need to allocate the buffer now. // It doesn't matter if the buffer is uniquely referenced or not here. let buffer = malloc(length) - memset(buffer, 1, length) + memset(buffer!, 1, length) let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) _pointer = result @@ -97,10 +97,10 @@ class TestNSData: XCTestCase { let newBuffer = malloc(newBufferLength) if let ptr = _pointer { // Copy the existing data to the new box, then return its pointer - memmove(newBuffer, ptr.baseAddress, newBufferLength) + memmove(newBuffer!, ptr.baseAddress, newBufferLength) } else { // Set new data to 1s - memset(newBuffer, 1, newBufferLength) + memset(newBuffer!, 1, newBufferLength) } let bytePtr = newBuffer!.bindMemory(to: UInt8.self, capacity: newBufferLength) let result = UnsafeMutableBufferPointer(start: bytePtr, count: newBufferLength) @@ -112,7 +112,7 @@ class TestNSData: XCTestCase { override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { if let d = _pointer { // Get the real data from the buffer - memmove(buffer, d.baseAddress, length) + memmove(buffer, d.baseAddress!, length) } else { // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes memset(buffer, 1, length) @@ -663,7 +663,7 @@ extension TestNSData { // Create a home for our test data let x = NSString() - let dirPath = try! (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) + let dirPath = (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) try! FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) let filePath = (dirPath.bridge()).stringByAppendingPathComponent("temp_file") guard FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil) else { XCTAssertTrue(false, "Unable to create temporary file"); return} From 195448d237355ca4befe9d36cb755ec473cbff23 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Tue, 16 Aug 2016 18:53:29 -0700 Subject: [PATCH 05/12] Make AllOnesData visible to linker --- TestFoundation/TestNSData.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 81cf836952..8a5e4c79a2 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -18,7 +18,7 @@ class TestNSData: XCTestCase { // This is a type of Data that starts off as a storage of all 0x01s, but it only creates that buffer when needed. When mutated it converts into a more traditional data storage backed by a buffer. - class AllOnesData : NSMutableData { + public class AllOnesData : NSMutableData { private var _length : Int = 0 var _pointer : UnsafeMutableBufferPointer? = nil { @@ -38,13 +38,13 @@ class TestNSData: XCTestCase { _pointer = nil super.init() } - convenience init?(length : Int) { + public convenience init?(length : Int) { self.init() _length = length _pointer = nil } - required init?(coder aDecoder: NSCoder) { + public required init?(coder aDecoder: NSCoder) { // Not tested fatalError() } @@ -55,7 +55,7 @@ class TestNSData: XCTestCase { } } - override var length : Int { + public override var length : Int { get { return _length } @@ -77,7 +77,7 @@ class TestNSData: XCTestCase { } } - override var bytes : UnsafeRawPointer { + public override var bytes : UnsafeRawPointer { if let d = _pointer { return UnsafeRawPointer(d.baseAddress!) } else { @@ -92,12 +92,12 @@ class TestNSData: XCTestCase { } } - override var mutableBytes: UnsafeMutableRawPointer { + override public var mutableBytes: UnsafeMutableRawPointer { let newBufferLength = _length let newBuffer = malloc(newBufferLength) if let ptr = _pointer { // Copy the existing data to the new box, then return its pointer - memmove(newBuffer!, ptr.baseAddress, newBufferLength) + memmove(newBuffer!, ptr.baseAddress!, newBufferLength) } else { // Set new data to 1s memset(newBuffer!, 1, newBufferLength) @@ -109,7 +109,7 @@ class TestNSData: XCTestCase { return UnsafeMutableRawPointer(result.baseAddress!) } - override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { + override public func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { if let d = _pointer { // Get the real data from the buffer memmove(buffer, d.baseAddress!, length) From f1f054d9095dd85b1d864eaed9145e19bb7204ea Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Tue, 16 Aug 2016 19:59:14 -0600 Subject: [PATCH 06/12] Eliminate the AllOnesData subclass (for now) hitting linker problems on linux --- TestFoundation/TestNSData.swift | 164 +------------------------------- 1 file changed, 3 insertions(+), 161 deletions(-) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 8a5e4c79a2..2b989483ac 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -17,109 +17,7 @@ class TestNSData: XCTestCase { - // This is a type of Data that starts off as a storage of all 0x01s, but it only creates that buffer when needed. When mutated it converts into a more traditional data storage backed by a buffer. - public class AllOnesData : NSMutableData { - - private var _length : Int = 0 - var _pointer : UnsafeMutableBufferPointer? = nil { - willSet { - if let p = _pointer { free(p.baseAddress) } - } - } - public override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { - _length = length - _pointer = nil - super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) - } - - - public override init() { - _length = 0 - _pointer = nil - super.init() - } - public convenience init?(length : Int) { - self.init() - _length = length - _pointer = nil - } - - public required init?(coder aDecoder: NSCoder) { - // Not tested - fatalError() - } - - deinit { - if let p = _pointer { - free(p.baseAddress) - } - } - - public override var length : Int { - get { - return _length - } - set { - if let ptr = _pointer { - // Copy the data to our new length buffer - let newBuffer = malloc(newValue)! - if newValue <= _length { - memmove(newBuffer, ptr.baseAddress!, newValue) - } else if newValue > _length { - memmove(newBuffer, ptr.baseAddress!, _length) - memset(newBuffer + _length, 1, newValue - _length) - } - let bytePtr = newBuffer.bindMemory(to: UInt8.self, capacity: newValue) - _pointer = UnsafeMutableBufferPointer(start: bytePtr, count: newValue) - } else { - _length = newValue - } - } - } - - public override var bytes : UnsafeRawPointer { - if let d = _pointer { - return UnsafeRawPointer(d.baseAddress!) - } else { - // Need to allocate the buffer now. - // It doesn't matter if the buffer is uniquely referenced or not here. - let buffer = malloc(length) - memset(buffer!, 1, length) - let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) - let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) - _pointer = result - return UnsafeRawPointer(result.baseAddress!) - } - } - - override public var mutableBytes: UnsafeMutableRawPointer { - let newBufferLength = _length - let newBuffer = malloc(newBufferLength) - if let ptr = _pointer { - // Copy the existing data to the new box, then return its pointer - memmove(newBuffer!, ptr.baseAddress!, newBufferLength) - } else { - // Set new data to 1s - memset(newBuffer!, 1, newBufferLength) - } - let bytePtr = newBuffer!.bindMemory(to: UInt8.self, capacity: newBufferLength) - let result = UnsafeMutableBufferPointer(start: bytePtr, count: newBufferLength) - _pointer = result - _length = newBufferLength - return UnsafeMutableRawPointer(result.baseAddress!) - } - - override public func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { - if let d = _pointer { - // Get the real data from the buffer - memmove(buffer, d.baseAddress!, length) - } else { - // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes - memset(buffer, 1, length) - } - } - } - + // MARK: - // String of course has its own way to get data, but this way tests our own data struct @@ -144,14 +42,12 @@ class TestNSData: XCTestCase { ("test_genericBuffers", test_genericBuffers), ("test_writeFailure", test_writeFailure), ("testBasicConstruction", testBasicConstruction), - ("testBridgingCustom", testBridgingCustom), ("testBridgingDefault", testBridgingDefault), ("testBridgingMutable", testBridgingMutable), ("testCopyBytes_oversized", testCopyBytes_oversized), ("testCopyBytes_ranges", testCopyBytes_ranges), ("testCopyBytes_undersized", testCopyBytes_undersized), // ("testCopyBytes", testCopyBytes), Disabled to due failure, we want this passing - but API matching is more important right now - ("testCustomData", testCustomData), ("testCustomDeallocator", testCustomDeallocator), ("testDataInSet", testDataInSet), ("testEquality", testEquality), @@ -601,32 +497,7 @@ extension TestNSData { } } - func testCustomData() { - let length = 5 - let allOnesData = Data(referencing: AllOnesData(length: length)!) - XCTAssertEqual(1, allOnesData[0], "First byte of all 1s data should be 1") - - // Double the length - var allOnesCopyToMutate = allOnesData - allOnesCopyToMutate.count = allOnesData.count * 2 - - XCTAssertEqual(allOnesData.count, length, "The length of the initial data should not have changed") - XCTAssertEqual(allOnesCopyToMutate.count, length * 2, "The length should have changed") - - // Force the second data to create its storage - allOnesCopyToMutate.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in - XCTAssertEqual(bytes.pointee, 1, "First byte should be 1") - - // Mutate the second data - bytes.pointee = 0 - XCTAssertEqual(bytes.pointee, 0, "First byte should be 0") - XCTAssertEqual(allOnesCopyToMutate[0], 0, "First byte accessed via other method should still be 0") - - // Verify that the first data is still 1 - XCTAssertEqual(allOnesData[0], 1, "The first byte should still be 1") - } - - } + func testBridgingDefault() { let hello = dataFrom("hello") @@ -654,36 +525,7 @@ extension TestNSData { } - func testBridgingCustom() { - // Let's use an AllOnesData with some Objective-C code - let allOnes = AllOnesData(length: 64)! - - // Type-erased - let data = Data(referencing: allOnes) - - // Create a home for our test data - let x = NSString() - let dirPath = (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) - try! FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) - let filePath = (dirPath.bridge()).stringByAppendingPathComponent("temp_file") - guard FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil) else { XCTAssertTrue(false, "Unable to create temporary file"); return} - guard let fh = FileHandle(forWritingAtPath: filePath) else { XCTAssertTrue(false, "Unable to open temporary file"); return } - defer { try! FileManager.default.removeItem(atPath: dirPath) } - - // Now use this data with some Objective-C code that takes NSData arguments - fh.write(data) - - // Get the data back - do { - let url = URL(fileURLWithPath: filePath) - let readData = try Data.init(contentsOf: url) - XCTAssertEqual(data.count, readData.count, "The length of the data is not the same") - } catch { - XCTAssertTrue(false, "Unable to read back data") - return - } - } - + func testEquality() { let d1 = dataFrom("hello") let d2 = dataFrom("hello") From 32eb0e75034e67283238931b1c7b61c2dcd9ffdc Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Wed, 17 Aug 2016 16:31:33 -0700 Subject: [PATCH 07/12] Revert "Eliminate the AllOnesData subclass (for now) hitting linker problems on linux" This reverts commit f1f054d9095dd85b1d864eaed9145e19bb7204ea. --- TestFoundation/TestNSData.swift | 164 +++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 3 deletions(-) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 2b989483ac..8a5e4c79a2 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -17,7 +17,109 @@ class TestNSData: XCTestCase { - + // This is a type of Data that starts off as a storage of all 0x01s, but it only creates that buffer when needed. When mutated it converts into a more traditional data storage backed by a buffer. + public class AllOnesData : NSMutableData { + + private var _length : Int = 0 + var _pointer : UnsafeMutableBufferPointer? = nil { + willSet { + if let p = _pointer { free(p.baseAddress) } + } + } + public override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { + _length = length + _pointer = nil + super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) + } + + + public override init() { + _length = 0 + _pointer = nil + super.init() + } + public convenience init?(length : Int) { + self.init() + _length = length + _pointer = nil + } + + public required init?(coder aDecoder: NSCoder) { + // Not tested + fatalError() + } + + deinit { + if let p = _pointer { + free(p.baseAddress) + } + } + + public override var length : Int { + get { + return _length + } + set { + if let ptr = _pointer { + // Copy the data to our new length buffer + let newBuffer = malloc(newValue)! + if newValue <= _length { + memmove(newBuffer, ptr.baseAddress!, newValue) + } else if newValue > _length { + memmove(newBuffer, ptr.baseAddress!, _length) + memset(newBuffer + _length, 1, newValue - _length) + } + let bytePtr = newBuffer.bindMemory(to: UInt8.self, capacity: newValue) + _pointer = UnsafeMutableBufferPointer(start: bytePtr, count: newValue) + } else { + _length = newValue + } + } + } + + public override var bytes : UnsafeRawPointer { + if let d = _pointer { + return UnsafeRawPointer(d.baseAddress!) + } else { + // Need to allocate the buffer now. + // It doesn't matter if the buffer is uniquely referenced or not here. + let buffer = malloc(length) + memset(buffer!, 1, length) + let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) + let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) + _pointer = result + return UnsafeRawPointer(result.baseAddress!) + } + } + + override public var mutableBytes: UnsafeMutableRawPointer { + let newBufferLength = _length + let newBuffer = malloc(newBufferLength) + if let ptr = _pointer { + // Copy the existing data to the new box, then return its pointer + memmove(newBuffer!, ptr.baseAddress!, newBufferLength) + } else { + // Set new data to 1s + memset(newBuffer!, 1, newBufferLength) + } + let bytePtr = newBuffer!.bindMemory(to: UInt8.self, capacity: newBufferLength) + let result = UnsafeMutableBufferPointer(start: bytePtr, count: newBufferLength) + _pointer = result + _length = newBufferLength + return UnsafeMutableRawPointer(result.baseAddress!) + } + + override public func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { + if let d = _pointer { + // Get the real data from the buffer + memmove(buffer, d.baseAddress!, length) + } else { + // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes + memset(buffer, 1, length) + } + } + } + // MARK: - // String of course has its own way to get data, but this way tests our own data struct @@ -42,12 +144,14 @@ class TestNSData: XCTestCase { ("test_genericBuffers", test_genericBuffers), ("test_writeFailure", test_writeFailure), ("testBasicConstruction", testBasicConstruction), + ("testBridgingCustom", testBridgingCustom), ("testBridgingDefault", testBridgingDefault), ("testBridgingMutable", testBridgingMutable), ("testCopyBytes_oversized", testCopyBytes_oversized), ("testCopyBytes_ranges", testCopyBytes_ranges), ("testCopyBytes_undersized", testCopyBytes_undersized), // ("testCopyBytes", testCopyBytes), Disabled to due failure, we want this passing - but API matching is more important right now + ("testCustomData", testCustomData), ("testCustomDeallocator", testCustomDeallocator), ("testDataInSet", testDataInSet), ("testEquality", testEquality), @@ -497,7 +601,32 @@ extension TestNSData { } } - + func testCustomData() { + let length = 5 + let allOnesData = Data(referencing: AllOnesData(length: length)!) + XCTAssertEqual(1, allOnesData[0], "First byte of all 1s data should be 1") + + // Double the length + var allOnesCopyToMutate = allOnesData + allOnesCopyToMutate.count = allOnesData.count * 2 + + XCTAssertEqual(allOnesData.count, length, "The length of the initial data should not have changed") + XCTAssertEqual(allOnesCopyToMutate.count, length * 2, "The length should have changed") + + // Force the second data to create its storage + allOnesCopyToMutate.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in + XCTAssertEqual(bytes.pointee, 1, "First byte should be 1") + + // Mutate the second data + bytes.pointee = 0 + XCTAssertEqual(bytes.pointee, 0, "First byte should be 0") + XCTAssertEqual(allOnesCopyToMutate[0], 0, "First byte accessed via other method should still be 0") + + // Verify that the first data is still 1 + XCTAssertEqual(allOnesData[0], 1, "The first byte should still be 1") + } + + } func testBridgingDefault() { let hello = dataFrom("hello") @@ -525,7 +654,36 @@ extension TestNSData { } - + func testBridgingCustom() { + // Let's use an AllOnesData with some Objective-C code + let allOnes = AllOnesData(length: 64)! + + // Type-erased + let data = Data(referencing: allOnes) + + // Create a home for our test data + let x = NSString() + let dirPath = (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) + try! FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) + let filePath = (dirPath.bridge()).stringByAppendingPathComponent("temp_file") + guard FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil) else { XCTAssertTrue(false, "Unable to create temporary file"); return} + guard let fh = FileHandle(forWritingAtPath: filePath) else { XCTAssertTrue(false, "Unable to open temporary file"); return } + defer { try! FileManager.default.removeItem(atPath: dirPath) } + + // Now use this data with some Objective-C code that takes NSData arguments + fh.write(data) + + // Get the data back + do { + let url = URL(fileURLWithPath: filePath) + let readData = try Data.init(contentsOf: url) + XCTAssertEqual(data.count, readData.count, "The length of the data is not the same") + } catch { + XCTAssertTrue(false, "Unable to read back data") + return + } + } + func testEquality() { let d1 = dataFrom("hello") let d2 = dataFrom("hello") From 9497a1facd0315b60cee932e4b7c1529be513779 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Wed, 17 Aug 2016 16:31:38 -0700 Subject: [PATCH 08/12] Revert "Make AllOnesData visible to linker" This reverts commit 195448d237355ca4befe9d36cb755ec473cbff23. --- TestFoundation/TestNSData.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 8a5e4c79a2..81cf836952 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -18,7 +18,7 @@ class TestNSData: XCTestCase { // This is a type of Data that starts off as a storage of all 0x01s, but it only creates that buffer when needed. When mutated it converts into a more traditional data storage backed by a buffer. - public class AllOnesData : NSMutableData { + class AllOnesData : NSMutableData { private var _length : Int = 0 var _pointer : UnsafeMutableBufferPointer? = nil { @@ -38,13 +38,13 @@ class TestNSData: XCTestCase { _pointer = nil super.init() } - public convenience init?(length : Int) { + convenience init?(length : Int) { self.init() _length = length _pointer = nil } - public required init?(coder aDecoder: NSCoder) { + required init?(coder aDecoder: NSCoder) { // Not tested fatalError() } @@ -55,7 +55,7 @@ class TestNSData: XCTestCase { } } - public override var length : Int { + override var length : Int { get { return _length } @@ -77,7 +77,7 @@ class TestNSData: XCTestCase { } } - public override var bytes : UnsafeRawPointer { + override var bytes : UnsafeRawPointer { if let d = _pointer { return UnsafeRawPointer(d.baseAddress!) } else { @@ -92,12 +92,12 @@ class TestNSData: XCTestCase { } } - override public var mutableBytes: UnsafeMutableRawPointer { + override var mutableBytes: UnsafeMutableRawPointer { let newBufferLength = _length let newBuffer = malloc(newBufferLength) if let ptr = _pointer { // Copy the existing data to the new box, then return its pointer - memmove(newBuffer!, ptr.baseAddress!, newBufferLength) + memmove(newBuffer!, ptr.baseAddress, newBufferLength) } else { // Set new data to 1s memset(newBuffer!, 1, newBufferLength) @@ -109,7 +109,7 @@ class TestNSData: XCTestCase { return UnsafeMutableRawPointer(result.baseAddress!) } - override public func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { + override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { if let d = _pointer { // Get the real data from the buffer memmove(buffer, d.baseAddress!, length) From 4ceb9f199773c5dce5cdfb527278bbc65678f104 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Wed, 17 Aug 2016 16:31:39 -0700 Subject: [PATCH 09/12] Revert "And more linux fixes - this time for unit tests" This reverts commit 1b50c1662e42895a4459693a737cdc1b014b0d8f. --- TestFoundation/TestNSData.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 81cf836952..71e7b2e5b4 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -64,9 +64,9 @@ class TestNSData: XCTestCase { // Copy the data to our new length buffer let newBuffer = malloc(newValue)! if newValue <= _length { - memmove(newBuffer, ptr.baseAddress!, newValue) + memmove(newBuffer, ptr.baseAddress, newValue) } else if newValue > _length { - memmove(newBuffer, ptr.baseAddress!, _length) + memmove(newBuffer, ptr.baseAddress, _length) memset(newBuffer + _length, 1, newValue - _length) } let bytePtr = newBuffer.bindMemory(to: UInt8.self, capacity: newValue) @@ -84,7 +84,7 @@ class TestNSData: XCTestCase { // Need to allocate the buffer now. // It doesn't matter if the buffer is uniquely referenced or not here. let buffer = malloc(length) - memset(buffer!, 1, length) + memset(buffer, 1, length) let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) _pointer = result @@ -97,10 +97,10 @@ class TestNSData: XCTestCase { let newBuffer = malloc(newBufferLength) if let ptr = _pointer { // Copy the existing data to the new box, then return its pointer - memmove(newBuffer!, ptr.baseAddress, newBufferLength) + memmove(newBuffer, ptr.baseAddress, newBufferLength) } else { // Set new data to 1s - memset(newBuffer!, 1, newBufferLength) + memset(newBuffer, 1, newBufferLength) } let bytePtr = newBuffer!.bindMemory(to: UInt8.self, capacity: newBufferLength) let result = UnsafeMutableBufferPointer(start: bytePtr, count: newBufferLength) @@ -112,7 +112,7 @@ class TestNSData: XCTestCase { override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { if let d = _pointer { // Get the real data from the buffer - memmove(buffer, d.baseAddress!, length) + memmove(buffer, d.baseAddress, length) } else { // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes memset(buffer, 1, length) @@ -663,7 +663,7 @@ extension TestNSData { // Create a home for our test data let x = NSString() - let dirPath = (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) + let dirPath = try! (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) try! FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) let filePath = (dirPath.bridge()).stringByAppendingPathComponent("temp_file") guard FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil) else { XCTAssertTrue(false, "Unable to create temporary file"); return} From ad45263ba95539aea9bb4201ac9aeb58c3a03cc9 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Wed, 17 Aug 2016 16:31:40 -0700 Subject: [PATCH 10/12] Revert "Further linux build refinements" This reverts commit 7980e6ceb5df7373a3a3be9c4bf5ee13f4fe0fae. --- Foundation/Data.swift | 2 +- Foundation/NSData.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Foundation/Data.swift b/Foundation/Data.swift index 3f7823c349..46579d5f6a 100644 --- a/Foundation/Data.swift +++ b/Foundation/Data.swift @@ -270,7 +270,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// - parameter reference: The instance of `NSData` that you wish to wrap. This instance will be copied by `struct Data`. public init(referencing reference: NSData) { - _wrapped = _SwiftNSData(immutableObject: reference.copy() as! AnyObject) + _wrapped = _SwiftNSData(immutableObject: reference.copy() as AnyObject) } // ----------------------------------- diff --git a/Foundation/NSData.swift b/Foundation/NSData.swift index 17efa99862..5c55696478 100644 --- a/Foundation/NSData.swift +++ b/Foundation/NSData.swift @@ -233,7 +233,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { return false } - return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in + return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in let bytes1 = bytes return memcmp(bytes1, bytes2, length) == 0 } From bc34be840aba82c61610d8ccc587fd5f4beabc85 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Wed, 17 Aug 2016 16:31:41 -0700 Subject: [PATCH 11/12] Revert "Restore a few raw pointers uncovered by building on linux" This reverts commit a365e75cd8ebb77b47071b6bca6ff18f71664588. --- Foundation/Data.swift | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Foundation/Data.swift b/Foundation/Data.swift index 46579d5f6a..17e54aee3e 100644 --- a/Foundation/Data.swift +++ b/Foundation/Data.swift @@ -286,8 +286,12 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - private func _getUnsafeBytesPointer() -> UnsafeRawPointer { - return _mapUnmanaged { return $0.bytes } + private func _getUnsafeBytesPointer() -> UnsafePointer { + + return _mapUnmanaged { + let boundBytes = $0.bytes.bindMemory(to: Void.self, capacity: $0.length) + return boundBytes + } } /// Access the bytes in the data. @@ -296,13 +300,15 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public func withUnsafeBytes(_ body: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { let bytes = _getUnsafeBytesPointer() defer { _fixLifetime(self)} - let contentPtr = bytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) - return try body(contentPtr) + let result = try bytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { + return try body($0) + } + return result } - private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutableRawPointer { + private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutablePointer { return _applyUnmanagedMutation { - return $0.mutableBytes + return $0.mutableBytes.bindMemory(to: Void.self, capacity: count) } } @@ -313,8 +319,11 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { let mutableBytes = _getUnsafeMutableBytesPointer() defer { _fixLifetime(self)} - let contentPtr = mutableBytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) - return try body(contentPtr) + + let result = try mutableBytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { + return try body($0) + } + return result } // MARK: - From 624853075196e74efb586128b1ada4801eaf8274 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Wed, 17 Aug 2016 16:31:42 -0700 Subject: [PATCH 12/12] Revert "Swift 3 API Parity: Data & NSData" This reverts commit 6522414c9aa6cbe89f05786e98ab63bf6ebe4f73. --- Foundation/Data.swift | 214 ++--- Foundation/NSConcreteValue.swift | 3 +- Foundation/NSData.swift | 666 +++++++-------- Foundation/NSFileHandle.swift | 2 +- Foundation/NSFileManager.swift | 2 +- Foundation/NSString.swift | 6 +- Foundation/NSXMLDocument.swift | 2 +- Foundation/NSXMLParser.swift | 6 +- TestFoundation/TestNSData.swift | 819 +------------------ TestFoundation/TestNSJSONSerialization.swift | 7 +- TestFoundation/TestNSXMLParser.swift | 4 +- 11 files changed, 445 insertions(+), 1286 deletions(-) diff --git a/Foundation/Data.swift b/Foundation/Data.swift index 17e54aee3e..0c0ede6e10 100644 --- a/Foundation/Data.swift +++ b/Foundation/Data.swift @@ -73,15 +73,56 @@ internal final class _SwiftNSData : NSData, _SwiftNativeFoundationType { releaseWrappedObject() } - // MARK: - Funnel overrides + // Stubs + // ----- + override var length : Int { get { return _mapUnmanaged { $0.length } } } + override var bytes : UnsafeRawPointer { return _mapUnmanaged { $0.bytes } } + +// override func subdata(with range: NSRange) -> Data { +// return _mapUnmanaged { $0.subdata(with: range) } +// } +// +// override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { +// return _mapUnmanaged { $0.getBytes(buffer, length: length) } +// } +// +// override func getBytes(_ buffer: UnsafeMutableRawPointer, range: NSRange) { +// return _mapUnmanaged { $0.getBytes(buffer, range: range) } +// } +// +// override func isEqual(to other: Data) -> Bool { +// return _mapUnmanaged { return $0.isEqual(to: other) } +// } +// +// override func write(to url: URL, options: Data.WritingOptions) throws { +// return try _mapUnmanaged { try $0.write(to: url, options: options) } +// } +// +// override func range(of data: Data, options: Data.SearchOptions, range: NSRange) -> NSRange { +// return _mapUnmanaged { +// $0.range(of: data, options: options, in: range) +// } +// } +// +// override func enumerateByteRanges(using block: (UnsafeRawPointer, NSRange, UnsafeMutablePointer) -> Void) { +// return _mapUnmanaged { $0.enumerateBytes(block) } +// } +// +// override func base64EncodedString(options: Data.Base64EncodingOptions) -> String { +// return _mapUnmanaged { $0.base64EncodedString(options) } +// } +// +// override func base64EncodedData(options: Data.Base64EncodingOptions) -> Data { +// return _mapUnmanaged { $0.base64EncodedData(options) } +// } } /** @@ -156,17 +197,10 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H _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. /// /// - parameter bytes: An array of bytes to copy. - public init(bytes: [UInt8]) { + public init(bytes: Array) { _wrapped = bytes.withUnsafeBufferPointer { return _SwiftNSData(immutableObject: NSData(bytes: $0.baseAddress, length: $0.count)) } @@ -192,7 +226,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// - parameter capacity: The size of the data. public init(capacity: Int) { if let d = NSMutableData(capacity: capacity) { - _wrapped = _SwiftNSData(mutableObject: d) + _wrapped = _SwiftNSData(immutableObject: d) } else { fatalError("Unable to allocate data of the requested capacity") } @@ -227,7 +261,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// Returns nil when the input is not recognized as valid Base-64. /// - parameter base64String: The string to parse. - /// - parameter options: Encoding options. Default value is `[]`. + /// - parameter options: Decoding options. Default value is `[]`. public init?(base64Encoded base64String: String, options: Data.Base64DecodingOptions = []) { if let d = NSData(base64Encoded: base64String, options: Base64DecodingOptions(rawValue: options.rawValue)) { _wrapped = _SwiftNSData(immutableObject: d) @@ -254,23 +288,16 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// - parameter count: The number of bytes the data initially contains. public init(count: Int) { - if let d = NSMutableData(length: count) { - _wrapped = _SwiftNSData(mutableObject: d) + if let memory = calloc(1, count)?.bindMemory(to: UInt8.self, capacity: count) { + self.init(bytesNoCopy: memory, count: count, deallocator: .free) } else { fatalError("Unable to allocate data of the requested count") } } - - /// 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) { - _wrapped = _SwiftNSData(immutableObject: reference.copy() as AnyObject) + internal init(_bridged data: 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) } // ----------------------------------- @@ -286,12 +313,8 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - private func _getUnsafeBytesPointer() -> UnsafePointer { - - return _mapUnmanaged { - let boundBytes = $0.bytes.bindMemory(to: Void.self, capacity: $0.length) - return boundBytes - } + private func _getUnsafeBytesPointer() -> UnsafeRawPointer { + return _mapUnmanaged { return $0.bytes } } /// Access the bytes in the data. @@ -300,15 +323,13 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public func withUnsafeBytes(_ body: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { let bytes = _getUnsafeBytesPointer() defer { _fixLifetime(self)} - let result = try bytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { - return try body($0) - } - return result + let contentPtr = bytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) + return try body(contentPtr) } - private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutablePointer { + private mutating func _getUnsafeMutableBytesPointer() -> UnsafeMutableRawPointer { return _applyUnmanagedMutation { - return $0.mutableBytes.bindMemory(to: Void.self, capacity: count) + return $0.mutableBytes } } @@ -319,11 +340,8 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { let mutableBytes = _getUnsafeMutableBytesPointer() defer { _fixLifetime(self)} - - let result = try mutableBytes.withMemoryRebound(to: ContentType.self, capacity: count / MemoryLayout.stride) { - return try body($0) - } - return result + let contentPtr = mutableBytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout.stride) + return try body(contentPtr) } // MARK: - @@ -338,7 +356,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H _mapUnmanaged { $0.getBytes(pointer, length: count) } } - private func _copyBytesHelper(to pointer: UnsafeMutablePointer, from range: NSRange) { + private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: NSRange) { _mapUnmanaged { $0.getBytes(pointer, range: range) } } @@ -348,7 +366,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// - parameter range: The range in the `Data` to copy. /// - warning: This method does not verify that the contents at pointer have enough space to hold the required number of bytes. public func copyBytes(to pointer: UnsafeMutablePointer, from range: Range) { - _copyBytesHelper(to: pointer, from: NSRange(range)) + _copyBytesHelper(to: pointer, from: NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)) } /// Copy the contents of the data into a buffer. @@ -379,10 +397,8 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H guard !copyRange.isEmpty else { return 0 } let nsRange = NSMakeRange(copyRange.lowerBound, copyRange.upperBound - copyRange.lowerBound) - let ptr = buffer.baseAddress! - ptr.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { - _copyBytesHelper(to: $0, from: nsRange) - } + let pointer = UnsafeMutableRawPointer(buffer.baseAddress!) + _copyBytesHelper(to: pointer, from: nsRange) return copyRange.count } @@ -483,13 +499,10 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// Replace a region of bytes in the data with new data. /// - /// This will resize the data if required, to fit the entire contents of `data`. - /// - /// - precondition: The bounds of `subrange` must be valid indices of the collection. - /// - parameter subrange: The range in the data to replace. If `subrange.lowerBound == data.count && subrange.count == 0` then this operation is an append. + /// - parameter range: The range in the data to replace. /// - parameter data: The replacement data. - public mutating func replaceSubrange(_ subrange: Range, with data: Data) { - let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound) + public mutating func replaceBytes(in range: Range, with data: Data) { + let nsRange = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound) let cnt = data.count let bytes = data._getUnsafeBytesPointer() @@ -498,68 +511,6 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - /// Replace a region of bytes in the data with new bytes from a buffer. - /// - /// This will resize the data if required, to fit the entire contents of `buffer`. - /// - /// - precondition: The bounds of `subrange` must be valid indices of the collection. - /// - parameter subrange: The range in the data to replace. - /// - parameter buffer: The replacement bytes. - public mutating func replaceSubrange(_ subrange: Range, with buffer: UnsafeBufferPointer) { - let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound) - let bufferCount = buffer.count * MemoryLayout.stride - - _applyUnmanagedMutation { - $0.replaceBytes(in: nsRange, withBytes: buffer.baseAddress!, length: bufferCount) - } - - } - - /// Replace a region of bytes in the data with new bytes from a collection. - /// - /// This will resize the data if required, to fit the entire contents of `newElements`. - /// - /// - precondition: The bounds of `subrange` must be valid indices of the collection. - /// - parameter subrange: The range in the data to replace. - /// - parameter newElements: The replacement bytes. - public mutating func replaceSubrange(_ subrange: Range, with newElements: ByteCollection) where ByteCollection.Iterator.Element == Data.Iterator.Element { - - // Calculate this once, it may not be O(1) - let replacementCount : Int = numericCast(newElements.count) - let currentCount = self.count - let subrangeCount = subrange.count - - if currentCount < subrange.lowerBound + subrangeCount { - if subrangeCount == 0 { - preconditionFailure("location \(subrange.lowerBound) exceeds data count \(currentCount)") - } else { - preconditionFailure("range \(subrange) exceeds data count \(currentCount)") - } - } - - let resultCount = currentCount - subrangeCount + replacementCount - if resultCount != currentCount { - // This may realloc. - // In the future, if we keep the malloced pointer and count inside this struct/ref instead of deferring to NSData, we may be able to do this more efficiently. - self.count = resultCount - } - - let shift = resultCount - currentCount - let start = subrange.lowerBound - - self.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) -> () in - if shift != 0 { - let destination = bytes + start + replacementCount - let source = bytes + start + subrangeCount - memmove(destination, source, currentCount - start - subrangeCount) - } - - if replacementCount != 0 { - newElements._copyContents(initializing: bytes + start) - } - } - } - /// Return a new copy of the data in a specified range. /// /// - parameter range: The range to copy. @@ -575,16 +526,16 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H /// /// - parameter options: The options to use for the encoding. Default value is `[]`. /// - returns: The Base-64 encoded string. - public func base64EncodedString(options: Data.Base64EncodingOptions = []) -> String { - return _mapUnmanaged { $0.base64EncodedString(options: options) } + public func base64EncodedString(_ options: Data.Base64EncodingOptions = []) -> String { + return _mapUnmanaged { $0.base64EncodedString(options) } } /// Returns a Base-64 encoded `Data`. /// /// - parameter options: The options to use for the encoding. Default value is `[]`. /// - returns: The Base-64 encoded data. - public func base64EncodedData(options: Data.Base64EncodingOptions = []) -> Data { - return _mapUnmanaged { $0.base64EncodedData(options: options) } + public func base64EncodedData(_ options: Data.Base64EncodingOptions = []) -> Data { + return _mapUnmanaged { $0.base64EncodedData(options) } } // MARK: - @@ -605,6 +556,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H return _mapUnmanaged { $0.debugDescription } } + // MARK: - // MARK: - // MARK: Index and Subscript @@ -625,12 +577,20 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H } } - public subscript(bounds: Range) -> MutableRandomAccessSlice { + public subscript(bounds: Range) -> MutableRandomAccessSlice { get { return MutableRandomAccessSlice(base: self, bounds: bounds) } set { - replaceSubrange(bounds, with: newValue.base) + // Ideally this would be: + // replaceBytes(in: bounds, with: newValue._base) + // but we do not have access to _base due to 'internal' protection + // TODO: Use a custom Slice type so we have access to the underlying data + let arrayOfBytes = newValue.map { $0 } + arrayOfBytes.withUnsafeBufferPointer { + let otherData = Data(buffer: $0) + replaceBytes(in: bounds, with: otherData) + } } } @@ -660,14 +620,12 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H public func makeIterator() -> Data.Iterator { return IndexingIterator(_elements: self) } - - /// Returns `true` if the two `Data` arguments are equal. - public static func ==(d1 : Data, d2 : Data) -> Bool { - return d1._wrapped.isEqual(to: d2) - } } - +/// Returns `true` if the two `Data` arguments are equal. +public func ==(d1 : Data, d2 : Data) -> Bool { + return d1._wrapped.isEqual(to: d2) +} /// Provides bridging functionality for struct Data to class NSData and vice-versa. extension Data { @@ -681,11 +639,11 @@ extension Data { } public static func _forceBridgeFromObjectiveC(_ input: NSData, result: inout Data?) { - result = Data(referencing: input) + result = Data(_bridged: input) } public static func _conditionallyBridgeFromObjectiveC(_ input: NSData, result: inout Data?) -> Bool { - result = Data(referencing: input) + result = Data(_bridged: input) return true } diff --git a/Foundation/NSConcreteValue.swift b/Foundation/NSConcreteValue.swift index 2eacc68748..41dead6562 100644 --- a/Foundation/NSConcreteValue.swift +++ b/Foundation/NSConcreteValue.swift @@ -113,8 +113,7 @@ internal class NSConcreteValue : NSValue { } override var description : String { - let boundBytes = self.value.bindMemory(to: UInt8.self, capacity: self._size) - return Data(bytes: boundBytes, count: self._size).description + return Data(bytes: self.value, count: self._size).description } convenience required init?(coder aDecoder: NSCoder) { diff --git a/Foundation/NSData.swift b/Foundation/NSData.swift index 5c55696478..9659815178 100644 --- a/Foundation/NSData.swift +++ b/Foundation/NSData.swift @@ -16,21 +16,22 @@ import Glibc #endif extension NSData { + public struct ReadingOptions : OptionSet { public let rawValue : UInt public init(rawValue: UInt) { self.rawValue = rawValue } - public static let mappedIfSafe = ReadingOptions(rawValue: UInt(1 << 0)) - public static let uncached = ReadingOptions(rawValue: UInt(1 << 1)) - public static let alwaysMapped = ReadingOptions(rawValue: UInt(1 << 2)) + public static let dataReadingMappedIfSafe = ReadingOptions(rawValue: UInt(1 << 0)) + public static let dataReadingUncached = ReadingOptions(rawValue: UInt(1 << 1)) + public static let dataReadingMappedAlways = ReadingOptions(rawValue: UInt(1 << 2)) } public struct WritingOptions : OptionSet { public let rawValue : UInt public init(rawValue: UInt) { self.rawValue = rawValue } - public static let atomic = WritingOptions(rawValue: UInt(1 << 0)) - public static let withoutOverwriting = WritingOptions(rawValue: UInt(1 << 1)) + public static let dataWritingAtomic = WritingOptions(rawValue: UInt(1 << 0)) + public static let dataWritingWithoutOverwriting = WritingOptions(rawValue: UInt(1 << 1)) } public struct SearchOptions : OptionSet { @@ -45,10 +46,10 @@ extension NSData { public let rawValue : UInt public init(rawValue: UInt) { self.rawValue = rawValue } - public static let lineLength64Characters = Base64EncodingOptions(rawValue: UInt(1 << 0)) - public static let lineLength76Characters = Base64EncodingOptions(rawValue: UInt(1 << 1)) - public static let endLineWithCarriageReturn = Base64EncodingOptions(rawValue: UInt(1 << 4)) - public static let endLineWithLineFeed = Base64EncodingOptions(rawValue: UInt(1 << 5)) + public static let encoding64CharacterLineLength = Base64EncodingOptions(rawValue: UInt(1 << 0)) + public static let encoding76CharacterLineLength = Base64EncodingOptions(rawValue: UInt(1 << 1)) + public static let encodingEndLineWithCarriageReturn = Base64EncodingOptions(rawValue: UInt(1 << 4)) + public static let encodingEndLineWithLineFeed = Base64EncodingOptions(rawValue: UInt(1 << 5)) } public struct Base64DecodingOptions : OptionSet { @@ -73,7 +74,6 @@ private let __kCFAllocatesCollectable: CFOptionFlags = 0x20 open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { typealias CFType = CFData - private var _base = _CFInfo(typeID: CFDataGetTypeID()) private var _length: CFIndex = 0 private var _capacity: CFIndex = 0 @@ -90,110 +90,21 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } - override open var _cfTypeID: CFTypeID { - return CFDataGetTypeID() - } - - public init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { - super.init() - let options : CFOptionFlags = (type(of: self) == NSMutableData.self) ? __kCFMutable | __kCFGrowable : 0x0 - let bytePtr = bytes?.bindMemory(to: UInt8.self, capacity: length) - if copy { - _CFDataInit(unsafeBitCast(self, to: CFMutableData.self), options, length, bytePtr, length, false) - if let handler = deallocator { - handler(bytes!, length) - } - } else { - if let handler = deallocator { - _deallocHandler!.handler = handler - } - // The data initialization should flag that CF should not deallocate which leaves the handler a chance to deallocate instead - _CFDataInit(unsafeBitCast(self, to: CFMutableData.self), options | __kCFDontDeallocate, length, bytePtr, length, true) - } - } - - public override convenience init() { + public override required convenience init() { let dummyPointer = unsafeBitCast(NSData.self, to: UnsafeMutableRawPointer.self) self.init(bytes: dummyPointer, length: 0, copy: false, deallocator: nil) } - public convenience init(bytes: UnsafeRawPointer?, length: Int) { - self.init(bytes: UnsafeMutableRawPointer(mutating: bytes), length: length, copy: true, deallocator: nil) - } - - public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int) { - self.init(bytes: bytes, length: length, copy: false, deallocator: nil) - } - - public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, freeWhenDone b: Bool) { - self.init(bytes: bytes, length: length, copy: false) { buffer, length in - if b { - free(buffer) - } - } - } - - public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { - self.init(bytes: bytes, length: length, copy: false, deallocator: deallocator) - } - public convenience init(contentsOfFile path: String, options readOptionsMask: ReadingOptions = []) throws { - let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: readOptionsMask) - self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) - } - - public convenience init?(contentsOfFile path: String) { - do { - let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: []) - self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) - } catch { - return nil - } - } - - public convenience init(data: Data) { - self.init(bytes:data._nsObject.bytes, length: data.count) + open override var hash: Int { + return Int(bitPattern: CFHash(_cfObject)) } - public convenience init(contentsOf url: URL, options readOptionsMask: ReadingOptions = []) throws { - if url.isFileURL { - try self.init(contentsOfFile: url.path!, options: readOptionsMask) + open override func isEqual(_ object: AnyObject?) -> Bool { + if let data = object as? NSData { + return self.isEqual(to: data._swiftObject) } else { - let session = URLSession(configuration: URLSessionConfiguration.defaultSessionConfiguration()) - let cond = NSCondition() - var resError: NSError? - var resData: Data? - let task = session.dataTaskWithURL(url, completionHandler: { (data: Data?, response: URLResponse?, error: NSError?) -> Void in - resData = data - resError = error - cond.broadcast() - }) - task.resume() - cond.wait() - if resData == nil { - throw resError! - } - self.init(data: resData!) - } - } - - public convenience init?(base64Encoded base64String: String, options: Base64DecodingOptions = []) { - let encodedBytes = Array(base64String.utf8) - guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { - return nil - } - self.init(bytes: decodedBytes, length: decodedBytes.count) - } - - - /* Create an NSData from a Base-64, UTF-8 encoded NSData. By default, returns nil when the input is not recognized as valid Base-64. - */ - public convenience init?(base64Encoded base64Data: Data, options: Base64DecodingOptions = []) { - var encodedBytes = [UInt8](repeating: 0, count: base64Data.count) - base64Data._nsObject.getBytes(&encodedBytes, length: encodedBytes.count) - guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { - return nil + return false } - self.init(bytes: decodedBytes, length: decodedBytes.count) } deinit { @@ -205,39 +116,31 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } - // MARK: - Funnel methods + internal init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { + super.init() + let options : CFOptionFlags = (type(of: self) == NSMutableData.self) ? __kCFMutable | __kCFGrowable : 0x0 + let bytePtr = bytes?.bindMemory(to: UInt8.self, capacity: length) + if copy { + _CFDataInit(unsafeBitCast(self, to: CFMutableData.self), options, length, bytePtr, length, false) + if let handler = deallocator { + handler(bytes!, length) + } + } else { + if let handler = deallocator { + _deallocHandler!.handler = handler + } + // The data initialization should flag that CF should not deallocate which leaves the handler a chance to deallocate instead + _CFDataInit(unsafeBitCast(self, to: CFMutableData.self), options | __kCFDontDeallocate, length, bytePtr, length, true) + } + } + open var length: Int { return CFDataGetLength(_cfObject) } - + open var bytes: UnsafeRawPointer { return UnsafeRawPointer(CFDataGetBytePtr(_cfObject)) } - - - - // MARK: - NSObject methods - open override var hash: Int { - return Int(bitPattern: CFHash(_cfObject)) - } - - open override func isEqual(_ object: AnyObject?) -> Bool { - if let data = object as? NSData { - return self.isEqual(to: data._swiftObject) - } else { - return false - } - } - open func isEqual(to other: Data) -> Bool { - if length != other.count { - return false - } - - return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in - let bytes1 = bytes - return memcmp(bytes1, bytes2, length) == 0 - } - } open override func copy() -> Any { return copy(with: nil) @@ -254,6 +157,42 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { open func mutableCopy(with zone: NSZone? = nil) -> Any { return NSMutableData(bytes: UnsafeMutableRawPointer(mutating: bytes), length: length, copy: true, deallocator: nil) } + + open func encode(with aCoder: NSCoder) { + if let aKeyedCoder = aCoder as? NSKeyedArchiver { + aKeyedCoder._encodePropertyList(self, forKey: "NS.data") + } else { + let bytePtr = self.bytes.bindMemory(to: UInt8.self, capacity: self.length) + aCoder.encodeBytes(bytePtr, length: self.length) + } + } + + public required convenience init?(coder aDecoder: NSCoder) { + if !aDecoder.allowsKeyedCoding { + if let data = aDecoder.decodeDataObject() { + self.init(data: data) + } else { + return nil + } + } else if type(of: aDecoder) == NSKeyedUnarchiver.self || aDecoder.containsValue(forKey: "NS.data") { + guard let data = aDecoder._decodePropertyListForKey("NS.data") as? NSData else { + return nil + } + self.init(data: data._swiftObject) + } else { + let result : Data? = aDecoder.withDecodedUnsafeBufferPointer(forKey: "NS.bytes") { + guard let buffer = $0 else { return nil } + return Data(buffer: buffer) + } + + guard let r = result else { return nil } + self.init(data: r) + } + } + + public static var supportsSecureCoding: Bool { + return true + } private func byteDescription(limit: Int? = nil) -> String { var s = "" @@ -287,45 +226,34 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { return "<\(byteDescription())>" } + override open var _cfTypeID: CFTypeID { + return CFDataGetTypeID() + } +} + +extension NSData { - // MARK: - NSCoding methods - open func encode(with aCoder: NSCoder) { - if let aKeyedCoder = aCoder as? NSKeyedArchiver { - aKeyedCoder._encodePropertyList(self, forKey: "NS.data") - } else { - let bytePtr = self.bytes.bindMemory(to: UInt8.self, capacity: self.length) - aCoder.encodeBytes(bytePtr, length: self.length) - } + public convenience init(bytes: UnsafeRawPointer?, length: Int) { + self.init(bytes: UnsafeMutableRawPointer(mutating: bytes), length: length, copy: true, deallocator: nil) + } + + public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int) { + self.init(bytes: bytes, length: length, copy: false, deallocator: nil) } - public required convenience init?(coder aDecoder: NSCoder) { - if !aDecoder.allowsKeyedCoding { - if let data = aDecoder.decodeDataObject() { - self.init(data: data) - } else { - return nil - } - } else if type(of: aDecoder) == NSKeyedUnarchiver.self || aDecoder.containsValue(forKey: "NS.data") { - guard let data = aDecoder._decodePropertyListForKey("NS.data") as? NSData else { - return nil - } - self.init(data: data._swiftObject) - } else { - let result : Data? = aDecoder.withDecodedUnsafeBufferPointer(forKey: "NS.bytes") { - guard let buffer = $0 else { return nil } - return Data(buffer: buffer) + public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, freeWhenDone b: Bool) { + self.init(bytes: bytes, length: length, copy: false) { buffer, length in + if b { + free(buffer) } - - guard let r = result else { return nil } - self.init(data: r) } } - - public static var supportsSecureCoding: Bool { - return true - } - // MARK: - IO + public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { + self.init(bytes: bytes, length: length, copy: false, deallocator: deallocator) + } + + internal struct NSDataReadResult { var bytes: UnsafeMutableRawPointer var length: Int @@ -355,7 +283,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { let length = Int(info.st_size) - if options.contains(.alwaysMapped) { + if options.contains(.dataReadingMappedAlways) { let data = mmap(nil, length, PROT_READ, MAP_PRIVATE, fd, 0) // Swift does not currently expose MAP_FAILURE @@ -388,7 +316,90 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } - internal func makeTemporaryFile(inDirectory dirPath: String) throws -> (Int32, String) { + public convenience init(contentsOfFile path: String, options readOptionsMask: ReadingOptions) throws { + let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: readOptionsMask) + self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) + } + + public convenience init?(contentsOfFile path: String) { + do { + let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: []) + self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator) + } catch { + return nil + } + } + + public convenience init(data: Data) { + self.init(bytes:data._nsObject.bytes, length: data.count) + } + + public convenience init(contentsOf url: URL, options readOptionsMask: ReadingOptions) throws { + if url.isFileURL { + try self.init(contentsOfFile: url.path!, options: readOptionsMask) + } else { + let session = URLSession(configuration: URLSessionConfiguration.defaultSessionConfiguration()) + let cond = NSCondition() + var resError: NSError? + var resData: Data? + let task = session.dataTaskWithURL(url, completionHandler: { (data: Data?, response: URLResponse?, error: NSError?) -> Void in + resData = data + resError = error + cond.broadcast() + }) + task.resume() + cond.wait() + if resData == nil { + throw resError! + } + self.init(data: resData!) + } + } + + public convenience init?(contentsOfURL url: URL) { + do { + try self.init(contentsOf: url, options: []) + } catch { + return nil + } + } +} + +extension NSData { + public func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { + let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: length) + CFDataGetBytes(_cfObject, CFRangeMake(0, length), bytePtr) + } + + public func getBytes(_ buffer: UnsafeMutableRawPointer, range: NSRange) { + let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: range.length) + CFDataGetBytes(_cfObject, CFRangeMake(range.location, range.length), bytePtr) + } + + public func isEqual(to other: Data) -> Bool { + + if length != other.count { + return false + } + + + return other.withUnsafeBytes { (bytes2: UnsafePointer) -> Bool in + let bytes1 = bytes + return memcmp(bytes1, bytes2, length) == 0 + } + } + + public func subdata(with range: NSRange) -> Data { + if range.length == 0 { + return Data() + } + if range.location == 0 && range.length == self.length { + return Data(_bridged: self) + } + return Data(bytes: bytes.advanced(by: range.location), count: range.length) + } + + internal func makeTemporaryFileInDirectory(_ dirPath: String) throws -> (Int32, String) { let template = dirPath._nsObject.stringByAppendingPathComponent("tmp.XXXXXX") let maxLength = Int(PATH_MAX) + 1 var buf = [Int8](repeating: 0, count: maxLength) @@ -401,7 +412,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { return (fd, pathResult) } - internal class func write(toFileDescriptor fd: Int32, path: String? = nil, buf: UnsafeRawPointer, length: Int) throws { + internal class func writeToFileDescriptor(_ fd: Int32, path: String? = nil, buf: UnsafeRawPointer, length: Int) throws { var bytesRemaining = length while bytesRemaining > 0 { var bytesWritten : Int @@ -420,10 +431,10 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } - open func write(toFile path: String, options writeOptionsMask: WritingOptions = []) throws { + public func write(toFile path: String, options writeOptionsMask: WritingOptions = []) throws { var fd : Int32 var mode : mode_t? = nil - let useAuxiliaryFile = writeOptionsMask.contains(.atomic) + let useAuxiliaryFile = writeOptionsMask.contains(.dataWritingAtomic) var auxFilePath : String? = nil if useAuxiliaryFile { // Preserve permissions. @@ -433,13 +444,13 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } else if errno != ENOENT && errno != ENAMETOOLONG { throw _NSErrorWithErrno(errno, reading: false, path: path) } - let (newFD, path) = try self.makeTemporaryFile(inDirectory: path._nsObject.stringByDeletingLastPathComponent) + let (newFD, path) = try self.makeTemporaryFileInDirectory(path._nsObject.stringByDeletingLastPathComponent) fd = newFD auxFilePath = path fchmod(fd, 0o666) } else { var flags = O_WRONLY | O_CREAT | O_TRUNC - if writeOptionsMask.contains(.withoutOverwriting) { + if writeOptionsMask.contains(.dataWritingWithoutOverwriting) { flags |= O_EXCL } fd = _CFOpenFileWithMode(path, flags, 0o666) @@ -454,7 +465,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { try self.enumerateByteRangesUsingBlockRethrows { (buf, range, stop) in if range.length > 0 { do { - try NSData.write(toFileDescriptor: fd, path: path, buf: buf, length: range.length) + try NSData.writeToFileDescriptor(fd, path: path, buf: buf, length: range.length) if fsync(fd) < 0 { throw _NSErrorWithErrno(errno, reading: false, path: path) } @@ -481,18 +492,16 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } - /// NOTE: the 'atomically' flag is ignored if the url is not of a type the supports atomic writes - open func write(toFile path: String, atomically useAuxiliaryFile: Bool) -> Bool { + public func write(toFile path: String, atomically useAuxiliaryFile: Bool) -> Bool { do { - try write(toFile: path, options: useAuxiliaryFile ? .atomic : []) + try write(toFile: path, options: useAuxiliaryFile ? .dataWritingAtomic : []) } catch { return false } return true } - /// NOTE: the 'atomically' flag is ignored if the url is not of a type the supports atomic writes - open func write(to url: URL, atomically: Bool) -> Bool { + public func write(to url: URL, atomically: Bool) -> Bool { if url.isFileURL { if let path = url.path { return write(toFile: path, atomically: atomically) @@ -509,7 +518,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { /// - throws: This method returns Void and is marked with the `throws` keyword to indicate that it throws an error in the event of failure. /// /// This method is invoked in a `try` expression and the caller is responsible for handling any errors in the `catch` clauses of a `do` statement, as described in [Error Handling](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42) in [The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097) and [Error Handling](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10) in [Using Swift with Cocoa and Objective-C](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216). - open func write(to url: URL, options writeOptionsMask: WritingOptions = []) throws { + public func write(to url: URL, options writeOptionsMask: WritingOptions = []) throws { guard let path = url.path, url.isFileURL == true else { let userInfo = [NSLocalizedDescriptionKey : "The folder at “\(url)” does not exist or is not a file URL.", // NSLocalizedString() not yet available NSURLErrorKey : url.absoluteString ?? ""] as Dictionary @@ -518,31 +527,8 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { try write(toFile: path, options: writeOptionsMask) } - - // MARK: - Bytes - open func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { - let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: length) - CFDataGetBytes(_cfObject, CFRangeMake(0, length), bytePtr) - } - - open func getBytes(_ buffer: UnsafeMutableRawPointer, range: NSRange) { - let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: range.length) - CFDataGetBytes(_cfObject, CFRangeMake(range.location, range.length), bytePtr) - } - - open func subdata(with range: NSRange) -> Data { - if range.length == 0 { - return Data() - } - if range.location == 0 && range.length == self.length { - return Data(referencing: self) - } - let p = self.bytes.advanced(by: range.location).bindMemory(to: UInt8.self, capacity: range.length) - return Data(bytes: p, count: range.length) - } - - open func range(of dataToFind: Data, options mask: SearchOptions = [], in searchRange: NSRange) -> NSRange { - let dataToFind = dataToFind._nsObject + public func range(of searchData: Data, options mask: SearchOptions = [], in searchRange: NSRange) -> NSRange { + let dataToFind = searchData._nsObject guard dataToFind.length > 0 else {return NSRange(location: NSNotFound, length: 0)} guard let searchRange = searchRange.toRange() else {fatalError("invalid range")} @@ -586,37 +572,118 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } } - /// 'block' is called once for each contiguous region of memory in the receiver (once total for contiguous NSDatas), until either all bytes have been enumerated, or the 'stop' parameter is set to true. - open func enumerateBytes(_ block: (UnsafeRawPointer, NSRange, UnsafeMutablePointer) -> Void) { + public func enumerateBytes(_ block: (UnsafeRawPointer, NSRange, UnsafeMutablePointer) -> Void) { var stop = false withUnsafeMutablePointer(to: &stop) { stopPointer in - if (stopPointer.pointee) { - return - } block(bytes, NSMakeRange(0, length), stopPointer) } } +} + +extension NSData : _CFBridgable, _SwiftBridgable { + typealias SwiftType = Data + internal var _swiftObject: SwiftType { return Data(_bridged: self) } + + public func bridge() -> Data { + return _swiftObject + } +} + +extension Data : _NSBridgable, _CFBridgable { + typealias CFType = CFData + typealias NSType = NSData + internal var _cfObject: CFType { return _nsObject._cfObject } + internal var _nsObject: NSType { return _bridgeToObjectiveC() } + + public func bridge() -> NSData { + return _nsObject + } +} + +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) } +} + +extension NSMutableData { + internal var _cfMutableObject: CFMutableData { return unsafeBitCast(self, to: CFMutableData.self) } +} + +open class NSMutableData : NSData { + + public required convenience init() { + self.init(bytes: nil, length: 0) + } + + internal override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) { + super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) + } + + open var mutableBytes: UnsafeMutableRawPointer { + return UnsafeMutableRawPointer(CFDataGetMutableBytePtr(_cfMutableObject)) + } + + open override var length: Int { + get { + return CFDataGetLength(_cfObject) + } + set { + CFDataSetLength(_cfMutableObject, newValue) + } + } - // MARK: - Base64 Methods + open override func copy(with zone: NSZone? = nil) -> Any { + return NSData(bytes: bytes, length: length) + } +} - /// Create a Base-64 encoded String from the receiver's contents using the given options. - open func base64EncodedString(options: Base64EncodingOptions = []) -> String { +extension NSData { + + /* Create an NSData from a Base-64 encoded NSString using the given options. By default, returns nil when the input is not recognized as valid Base-64. + */ + public convenience init?(base64Encoded base64String: String, options: Base64DecodingOptions) { + let encodedBytes = Array(base64String.utf8) + guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { + return nil + } + self.init(bytes: decodedBytes, length: decodedBytes.count) + } + + /* Create a Base-64 encoded NSString from the receiver's contents using the given options. + */ + public func base64EncodedString(_ options: Base64EncodingOptions = []) -> String { var decodedBytes = [UInt8](repeating: 0, count: self.length) getBytes(&decodedBytes, length: decodedBytes.count) let encodedBytes = NSData.base64EncodeBytes(decodedBytes, options: options) let characters = encodedBytes.map { Character(UnicodeScalar($0)) } return String(characters) } - - /// Create a Base-64, UTF-8 encoded Data from the receiver's contents using the given options. - open func base64EncodedData(options: Base64EncodingOptions = []) -> Data { + + /* Create an NSData from a Base-64, UTF-8 encoded NSData. By default, returns nil when the input is not recognized as valid Base-64. + */ + public convenience init?(base64Encoded base64Data: Data, options: Base64DecodingOptions) { + var encodedBytes = [UInt8](repeating: 0, count: base64Data.count) + base64Data._nsObject.getBytes(&encodedBytes, length: encodedBytes.count) + guard let decodedBytes = NSData.base64DecodeBytes(encodedBytes, options: options) else { + return nil + } + self.init(bytes: decodedBytes, length: decodedBytes.count) + } + + /* Create a Base-64, UTF-8 encoded NSData from the receiver's contents using the given options. + */ + public func base64EncodedData(_ options: Base64EncodingOptions = []) -> Data { var decodedBytes = [UInt8](repeating: 0, count: self.length) getBytes(&decodedBytes, length: decodedBytes.count) let encodedBytes = NSData.base64EncodeBytes(decodedBytes, options: options) return Data(bytes: encodedBytes, count: encodedBytes.count) } - - /// The ranges of ASCII characters that are used to encode data in Base64. + + /** + The ranges of ASCII characters that are used to encode data in Base64. + */ private static let base64ByteMappings: [Range] = [ 65 ..< 91, // A-Z 97 ..< 123, // a-z @@ -630,12 +697,12 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { private static let base64Padding : UInt8 = 61 // = /** - This method takes a byte with a character from Base64-encoded string - and gets the binary value that the character corresponds to. + This method takes a byte with a character from Base64-encoded string + and gets the binary value that the character corresponds to. - - parameter byte: The byte with the Base64 character. - - returns: Base64DecodedByte value containing the result (Valid , Invalid, Padding) - */ + - parameter byte: The byte with the Base64 character. + - returns: Base64DecodedByte value containing the result (Valid , Invalid, Padding) + */ private enum Base64DecodedByte { case valid(UInt8) case invalid @@ -655,15 +722,15 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } /** - This method takes six bits of binary data and encodes it as a character - in Base64. - - The value in the byte must be less than 64, because a Base64 character - can only represent 6 bits. - - - parameter byte: The byte to encode - - returns: The ASCII value for the encoded character. - */ + This method takes six bits of binary data and encodes it as a character + in Base64. + + The value in the byte must be less than 64, because a Base64 character + can only represent 6 bits. + + - parameter byte: The byte to encode + - returns: The ASCII value for the encoded character. + */ private static func base64EncodeByte(_ byte: UInt8) -> UInt8 { assert(byte < 64) var decodedStart: UInt8 = 0 @@ -679,19 +746,19 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { /** - This method decodes Base64-encoded data. + This method decodes Base64-encoded data. - If the input contains any bytes that are not valid Base64 characters, - this will return nil. - - - parameter bytes: The Base64 bytes - - parameter options: Options for handling invalid input - - returns: The decoded bytes. - */ + If the input contains any bytes that are not valid Base64 characters, + this will return nil. + + - parameter bytes: The Base64 bytes + - parameter options: Options for handling invalid input + - returns: The decoded bytes. + */ private static func base64DecodeBytes(_ bytes: [UInt8], options: Base64DecodingOptions = []) -> [UInt8]? { var decodedBytes = [UInt8]() decodedBytes.reserveCapacity((bytes.count/3)*2) - + var currentByte : UInt8 = 0 var validCharacterCount = 0 var paddingCount = 0 @@ -752,12 +819,12 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { /** - This method encodes data in Base64. + This method encodes data in Base64. - - parameter bytes: The bytes you want to encode - - parameter options: Options for formatting the result - - returns: The Base64-encoding for those bytes. - */ + - parameter bytes: The bytes you want to encode + - parameter options: Options for formatting the result + - returns: The Base64-encoding for those bytes. + */ private static func base64EncodeBytes(_ bytes: [UInt8], options: Base64EncodingOptions = []) -> [UInt8] { var result = [UInt8]() result.reserveCapacity((bytes.count/3)*4) @@ -765,15 +832,15 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { let lineOptions : (lineLength : Int, separator : [UInt8])? = { let lineLength: Int - if options.contains(.lineLength64Characters) { lineLength = 64 } - else if options.contains(.lineLength76Characters) { lineLength = 76 } + if options.contains(.encoding64CharacterLineLength) { lineLength = 64 } + else if options.contains(.encoding76CharacterLineLength) { lineLength = 76 } else { return nil } var separator = [UInt8]() - if options.contains(.endLineWithCarriageReturn) { separator.append(13) } - if options.contains(.endLineWithLineFeed) { separator.append(10) } + if options.contains(.encodingEndLineWithCarriageReturn) { separator.append(13) } + if options.contains(.encodingEndLineWithLineFeed) { separator.append(10) } //if the kind of line ending to insert is not specified, the default line ending is Carriage Return + Line Feed. if separator.count == 0 {separator = [13,10]} @@ -827,83 +894,16 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { } return result } - -} - -// MARK: - -extension NSData : _CFBridgable, _SwiftBridgable { - typealias SwiftType = Data - internal var _swiftObject: SwiftType { return Data(referencing: self) } - - public func bridge() -> Data { - return _swiftObject - } -} - -extension Data : _NSBridgable, _CFBridgable { - typealias CFType = CFData - typealias NSType = NSData - internal var _cfObject: CFType { return _nsObject._cfObject } - internal var _nsObject: NSType { return _bridgeToObjectiveC() } - - public func bridge() -> NSData { - return _nsObject - } } -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(referencing: self._nsObject) } -} +extension NSMutableData { -// MARK: - -open class NSMutableData : NSData { - internal var _cfMutableObject: CFMutableData { return unsafeBitCast(self, to: CFMutableData.self) } - - public override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { - super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) - } - public init() { - self.init(bytes: nil, length: 0) - } - - public convenience init?(capacity: Int) { - self.init(bytes: nil, length: 0) - } - - public convenience init?(length: Int) { - self.init(bytes: nil, length: 0) - self.length = length - } - - // MARK: - Funnel Methods - open var mutableBytes: UnsafeMutableRawPointer { - return UnsafeMutableRawPointer(CFDataGetMutableBytePtr(_cfMutableObject)) - } - - open override var length: Int { - get { - return CFDataGetLength(_cfObject) - } - set { - CFDataSetLength(_cfMutableObject, newValue) - } - } - - // MARK: - NSObject - open override func copy(with zone: NSZone? = nil) -> Any { - return NSData(bytes: bytes, length: length) - } - - // MARK: - Mutability - open func append(_ bytes: UnsafeRawPointer, length: Int) { + public func append(_ bytes: UnsafeRawPointer, length: Int) { let bytePtr = bytes.bindMemory(to: UInt8.self, capacity: length) CFDataAppendBytes(_cfMutableObject, bytePtr, length) } - open func append(_ other: Data) { + public func append(_ other: Data) { let otherLength = other.count other.withUnsafeBytes { append($0, length: otherLength) @@ -911,20 +911,20 @@ open class NSMutableData : NSData { } - open func increaseLength(by extraLength: Int) { + public func increaseLength(by extraLength: Int) { CFDataSetLength(_cfMutableObject, CFDataGetLength(_cfObject) + extraLength) } - open func replaceBytes(in range: NSRange, withBytes bytes: UnsafeRawPointer) { + public func replaceBytes(in range: NSRange, withBytes bytes: UnsafeRawPointer) { let bytePtr = bytes.bindMemory(to: UInt8.self, capacity: length) CFDataReplaceBytes(_cfMutableObject, CFRangeMake(range.location, range.length), bytePtr, length) } - open func resetBytes(in range: NSRange) { + public func resetBytes(in range: NSRange) { bzero(mutableBytes.advanced(by: range.location), range.length) } - open func setData(_ data: Data) { + public func setData(_ data: Data) { length = data.count data.withUnsafeBytes { replaceBytes(in: NSMakeRange(0, length), withBytes: $0) @@ -932,10 +932,20 @@ open class NSMutableData : NSData { } - open func replaceBytes(in range: NSRange, withBytes replacementBytes: UnsafeRawPointer?, length replacementLength: Int) { - if let replacementBytes = replacementBytes { - let bytePtr = replacementBytes.bindMemory(to: UInt8.self, capacity: replacementLength) - CFDataReplaceBytes(_cfMutableObject, CFRangeMake(range.location, range.length), bytePtr, replacementLength) - } + public func replaceBytes(in range: NSRange, withBytes replacementBytes: UnsafeRawPointer, length replacementLength: Int) { + let bytePtr = replacementBytes.bindMemory(to: UInt8.self, capacity: replacementLength) + CFDataReplaceBytes(_cfMutableObject, CFRangeMake(range.location, range.length), bytePtr, replacementLength) + } +} + +extension NSMutableData { + + public convenience init?(capacity: Int) { + self.init(bytes: nil, length: 0) + } + + public convenience init?(length: Int) { + self.init(bytes: nil, length: 0) + self.length = length } } diff --git a/Foundation/NSFileHandle.swift b/Foundation/NSFileHandle.swift index df2e935b8e..6812fecede 100644 --- a/Foundation/NSFileHandle.swift +++ b/Foundation/NSFileHandle.swift @@ -118,7 +118,7 @@ open class FileHandle: NSObject, NSSecureCoding { open func write(_ data: Data) { data.enumerateBytes() { (bytes, range, stop) in do { - try NSData.write(toFileDescriptor: self._fd, path: nil, buf: UnsafeRawPointer(bytes.baseAddress!), length: bytes.count) + try NSData.writeToFileDescriptor(self._fd, path: nil, buf: UnsafeRawPointer(bytes.baseAddress!), length: bytes.count) } catch { fatalError("Write failure") } diff --git a/Foundation/NSFileManager.swift b/Foundation/NSFileManager.swift index 92b192e117..8adc8ecf97 100644 --- a/Foundation/NSFileManager.swift +++ b/Foundation/NSFileManager.swift @@ -686,7 +686,7 @@ open class FileManager: NSObject { open func createFile(atPath path: String, contents data: Data?, attributes attr: [String : Any]? = [:]) -> Bool { do { - try (data ?? Data()).write(to: URL(fileURLWithPath: path), options: .atomic) + try (data ?? Data()).write(to: URL(fileURLWithPath: path), options: .dataWritingAtomic) return true } catch _ { return false diff --git a/Foundation/NSString.swift b/Foundation/NSString.swift index ae193f571f..b5f18fa6a8 100644 --- a/Foundation/NSString.swift +++ b/Foundation/NSString.swift @@ -831,7 +831,7 @@ extension NSString { return String.Encoding.unicode.rawValue } - public func data(using encoding: UInt, allowLossyConversion lossy: Bool = false) -> Data? { + public func data(using encoding: UInt, allowLossyConversion lossy: Bool) -> Data? { let len = length var reqSize = 0 @@ -844,7 +844,7 @@ extension NSString { if convertedLen != len { return nil // Not able to do it all... } - + if 0 < reqSize { var data = Data(count: reqSize) data.count = data.withUnsafeMutableBytes { (mutableBytes: UnsafeMutablePointer) -> Int in @@ -1160,7 +1160,7 @@ extension NSString { internal func _writeTo(_ url: URL, _ useAuxiliaryFile: Bool, _ enc: UInt) throws { var data = Data() try _getExternalRepresentation(&data, url, enc) - try data.write(to: url, options: useAuxiliaryFile ? .atomic : []) + try data.write(to: url, options: useAuxiliaryFile ? .dataWritingAtomic : []) } open func write(to url: URL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws { diff --git a/Foundation/NSXMLDocument.swift b/Foundation/NSXMLDocument.swift index ca82b6f065..f7b378907c 100644 --- a/Foundation/NSXMLDocument.swift +++ b/Foundation/NSXMLDocument.swift @@ -82,7 +82,7 @@ open class XMLDocument : XMLNode { @abstract Returns a document created from the contents of an XML or HTML URL. Connection problems such as 404, parse errors are returned in error. */ public convenience init(contentsOf url: URL, options: Options) throws { - let data = try Data(contentsOf: url, options: .mappedIfSafe) + let data = try Data(contentsOf: url, options: .dataReadingMappedIfSafe) try self.init(data: data, options: options) } diff --git a/Foundation/NSXMLParser.swift b/Foundation/NSXMLParser.swift index 6ab8595bdb..d6bed25d4e 100644 --- a/Foundation/NSXMLParser.swift +++ b/Foundation/NSXMLParser.swift @@ -381,7 +381,7 @@ internal func _NSXMLParserProcessingInstruction(_ ctx: _CFXMLInterface, target: internal func _NSXMLParserCdataBlock(_ ctx: _CFXMLInterface, value: UnsafePointer, len: Int32) -> Void { let parser = ctx.parser if let delegate = parser.delegate { - delegate.parser(parser, foundCDATA: Data(bytes: value, count: Int(len))) + delegate.parser(parser, foundCDATA: Data(bytes: UnsafeRawPointer(value), count: Int(len))) } } @@ -551,9 +551,9 @@ open class XMLParser : NSObject { _bomChunk = nil if (totalLength > 4) { - let remainingData = allExistingData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Data in + let remainingData = allExistingData.withUnsafeBytes { (bytes: UnsafePointer) -> Data in let ptr = bytes.advanced(by: 4) - return Data(bytesNoCopy: ptr, count: totalLength - 4, deallocator: .none) + return Data(bytesNoCopy: UnsafeMutablePointer(mutating: ptr), count: totalLength - 4, deallocator: .none) } let _ = parseData(remainingData) diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 71e7b2e5b4..7d6c39867d 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -17,157 +17,8 @@ class TestNSData: XCTestCase { - // This is a type of Data that starts off as a storage of all 0x01s, but it only creates that buffer when needed. When mutated it converts into a more traditional data storage backed by a buffer. - class AllOnesData : NSMutableData { - - private var _length : Int = 0 - var _pointer : UnsafeMutableBufferPointer? = nil { - willSet { - if let p = _pointer { free(p.baseAddress) } - } - } - public override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (@escaping (UnsafeMutableRawPointer, Int) -> Void)? = nil) { - _length = length - _pointer = nil - super.init(bytes: bytes, length: length, copy: copy, deallocator: deallocator) - } - - - public override init() { - _length = 0 - _pointer = nil - super.init() - } - convenience init?(length : Int) { - self.init() - _length = length - _pointer = nil - } - - required init?(coder aDecoder: NSCoder) { - // Not tested - fatalError() - } - - deinit { - if let p = _pointer { - free(p.baseAddress) - } - } - - override var length : Int { - get { - return _length - } - set { - if let ptr = _pointer { - // Copy the data to our new length buffer - let newBuffer = malloc(newValue)! - if newValue <= _length { - memmove(newBuffer, ptr.baseAddress, newValue) - } else if newValue > _length { - memmove(newBuffer, ptr.baseAddress, _length) - memset(newBuffer + _length, 1, newValue - _length) - } - let bytePtr = newBuffer.bindMemory(to: UInt8.self, capacity: newValue) - _pointer = UnsafeMutableBufferPointer(start: bytePtr, count: newValue) - } else { - _length = newValue - } - } - } - - override var bytes : UnsafeRawPointer { - if let d = _pointer { - return UnsafeRawPointer(d.baseAddress!) - } else { - // Need to allocate the buffer now. - // It doesn't matter if the buffer is uniquely referenced or not here. - let buffer = malloc(length) - memset(buffer, 1, length) - let bytePtr = buffer!.bindMemory(to: UInt8.self, capacity: length) - let result = UnsafeMutableBufferPointer(start: bytePtr, count: length) - _pointer = result - return UnsafeRawPointer(result.baseAddress!) - } - } - - override var mutableBytes: UnsafeMutableRawPointer { - let newBufferLength = _length - let newBuffer = malloc(newBufferLength) - if let ptr = _pointer { - // Copy the existing data to the new box, then return its pointer - memmove(newBuffer, ptr.baseAddress, newBufferLength) - } else { - // Set new data to 1s - memset(newBuffer, 1, newBufferLength) - } - let bytePtr = newBuffer!.bindMemory(to: UInt8.self, capacity: newBufferLength) - let result = UnsafeMutableBufferPointer(start: bytePtr, count: newBufferLength) - _pointer = result - _length = newBufferLength - return UnsafeMutableRawPointer(result.baseAddress!) - } - - override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) { - if let d = _pointer { - // Get the real data from the buffer - memmove(buffer, d.baseAddress, length) - } else { - // A more efficient implementation of getBytes in the case where no one has asked for our backing bytes - memset(buffer, 1, length) - } - } - } - - // MARK: - - - // String of course has its own way to get data, but this way tests our own data struct - func dataFrom(_ string : String) -> Data { - // Create a Data out of those bytes - return string.utf8CString.withUnsafeBufferPointer { (ptr) in - ptr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: ptr.count) { - // Subtract 1 so we don't get the null terminator byte. This matches NSString behavior. - return Data(bytes: $0, count: ptr.count - 1) - } - } - } - static var allTests: [(String, (TestNSData) -> () throws -> Void)] { return [ - ("testBasicConstruction", testBasicConstruction), - ("test_base64Data_medium", test_base64Data_medium), - ("test_base64Data_small", test_base64Data_small), - ("test_basicReadWrite", test_basicReadWrite), - ("test_bufferSizeCalculation", test_bufferSizeCalculation), - // ("test_dataHash", test_dataHash), Disabled due to lack of brdiging in swift runtime -- infinite loops - ("test_genericBuffers", test_genericBuffers), - ("test_writeFailure", test_writeFailure), - ("testBasicConstruction", testBasicConstruction), - ("testBridgingCustom", testBridgingCustom), - ("testBridgingDefault", testBridgingDefault), - ("testBridgingMutable", testBridgingMutable), - ("testCopyBytes_oversized", testCopyBytes_oversized), - ("testCopyBytes_ranges", testCopyBytes_ranges), - ("testCopyBytes_undersized", testCopyBytes_undersized), - // ("testCopyBytes", testCopyBytes), Disabled to due failure, we want this passing - but API matching is more important right now - ("testCustomData", testCustomData), - ("testCustomDeallocator", testCustomDeallocator), - ("testDataInSet", testDataInSet), - ("testEquality", testEquality), - ("testGenericAlgorithms", testGenericAlgorithms), - ("testInitializationWithArray", testInitializationWithArray), - ("testInsertData", testInsertData), - ("testLoops", testLoops), - ("testMutableData", testMutableData), - ("testRange", testRange), - ("testReplaceSubrange", testReplaceSubrange), - ("testReplaceSubrange2", testReplaceSubrange2), - ("testReplaceSubrange3", testReplaceSubrange3), - ("testReplaceSubrange4", testReplaceSubrange4), - ("testReplaceSubrange5", testReplaceSubrange5), - - ("test_description", test_description), ("test_emptyDescription", test_emptyDescription), ("test_longDescription", test_longDescription), @@ -200,7 +51,7 @@ class TestNSData: XCTestCase { let saveData = try! Data(contentsOf: Bundle.main.url(forResource: "Test", withExtension: "plist")!) let savePath = URL(fileURLWithPath: "/var/tmp/Test.plist") do { - try saveData.write(to: savePath, options: .atomic) + try saveData.write(to: savePath, options: .dataWritingAtomic) let fileManager = FileManager.default XCTAssertTrue(fileManager.fileExists(atPath: savePath.path!)) try! fileManager.removeItem(atPath: savePath.path!) @@ -347,7 +198,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData() + let encodedData = data.base64EncodedData([]) guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -362,7 +213,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData(options: [.lineLength64Characters, .endLineWithLineFeed]) + let encodedData = data.base64EncodedData([.encoding64CharacterLineLength, .encodingEndLineWithLineFeed]) guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -377,7 +228,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData(options: [.lineLength76Characters, .endLineWithCarriageReturn]) + let encodedData = data.base64EncodedData([.encoding76CharacterLineLength, .encodingEndLineWithCarriageReturn]) guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -392,7 +243,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedData = data.base64EncodedData(options: [.lineLength76Characters, .endLineWithCarriageReturn, .endLineWithLineFeed]) + let encodedData = data.base64EncodedData([.encoding76CharacterLineLength, .encodingEndLineWithCarriageReturn, .encodingEndLineWithLineFeed]) guard let encodedTextResult = String(data: encodedData, encoding: String.Encoding.ascii) else { XCTFail("Could not convert encoded data to an ASCII String") return @@ -407,7 +258,7 @@ class TestNSData: XCTestCase { XCTFail("Could not encode UTF-8 string") return } - let encodedTextResult = data.base64EncodedString() + let encodedTextResult = data.base64EncodedString([]) XCTAssertEqual(encodedTextResult, encodedText) } @@ -501,9 +352,9 @@ class TestNSData: XCTestCase { var data = Data(bytes: [0, 0, 0, 0, 0]) let newData = Data(bytes: [1, 2, 3, 4, 5]) - // test replaceSubrange(_, with:) + // test Data.replaceBytes(in:with:) XCTAssertFalse(data == newData) - data.replaceSubrange(data.startIndex..= 2 }) - XCTAssertEqual(2, data2.count) - - let data3 = Data(bytes: [1, 2, 3, 4, 5][1..<3]) - XCTAssertEqual(2, data3.count) - } - - func testMutableData() { - let hello = dataFrom("hello") - let helloLength = hello.count - XCTAssertEqual(hello[0], 0x68, "Unexpected first byte") - - // Double the length - var mutatingHello = hello - mutatingHello.count *= 2 - - XCTAssertEqual(hello.count, helloLength, "The length of the initial data should not have changed") - XCTAssertEqual(mutatingHello.count, helloLength * 2, "The length should have changed") - - // Get the underlying data for hello2 - mutatingHello.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in - XCTAssertEqual(bytes.pointee, 0x68, "First byte should be 0x68") - - // Mutate it - bytes.pointee = 0x67 - XCTAssertEqual(bytes.pointee, 0x67, "First byte should be 0x67") - XCTAssertEqual(mutatingHello[0], 0x67, "First byte accessed via other method should still be 0x67") - - // Verify that the first data is still correct - XCTAssertEqual(hello[0], 0x68, "The first byte should still be 0x68") - } - } - - func testCustomData() { - let length = 5 - let allOnesData = Data(referencing: AllOnesData(length: length)!) - XCTAssertEqual(1, allOnesData[0], "First byte of all 1s data should be 1") - - // Double the length - var allOnesCopyToMutate = allOnesData - allOnesCopyToMutate.count = allOnesData.count * 2 - - XCTAssertEqual(allOnesData.count, length, "The length of the initial data should not have changed") - XCTAssertEqual(allOnesCopyToMutate.count, length * 2, "The length should have changed") - - // Force the second data to create its storage - allOnesCopyToMutate.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer) in - XCTAssertEqual(bytes.pointee, 1, "First byte should be 1") - - // Mutate the second data - bytes.pointee = 0 - XCTAssertEqual(bytes.pointee, 0, "First byte should be 0") - XCTAssertEqual(allOnesCopyToMutate[0], 0, "First byte accessed via other method should still be 0") - - // Verify that the first data is still 1 - XCTAssertEqual(allOnesData[0], 1, "The first byte should still be 1") - } - - } - - func testBridgingDefault() { - let hello = dataFrom("hello") - // Convert from struct Data to NSData - if let s = NSString(data: hello, encoding: String.Encoding.utf8.rawValue) { - XCTAssertTrue(s.isEqual(to: "hello"), "The strings should be equal") - } - - // Convert from NSData to struct Data - let goodbye = dataFrom("goodbye") - if let resultingData = NSString(string: "goodbye").data(using: String.Encoding.utf8.rawValue) { - XCTAssertEqual(resultingData[0], goodbye[0], "First byte should be equal") - } - } - - func testBridgingMutable() { - // Create a mutable data - var helloWorld = dataFrom("hello") - helloWorld.append(dataFrom("world")) - - // Convert from struct Data to NSData - if let s = NSString(data: helloWorld, encoding: String.Encoding.utf8.rawValue) { - XCTAssertTrue(s.isEqual(to: "helloworld"), "The strings should be equal") - } - - } - - func testBridgingCustom() { - // Let's use an AllOnesData with some Objective-C code - let allOnes = AllOnesData(length: 64)! - - // Type-erased - let data = Data(referencing: allOnes) - - // Create a home for our test data - let x = NSString() - let dirPath = try! (NSTemporaryDirectory().bridge()).stringByAppendingPathComponent(NSUUID().uuidString) - try! FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil) - let filePath = (dirPath.bridge()).stringByAppendingPathComponent("temp_file") - guard FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil) else { XCTAssertTrue(false, "Unable to create temporary file"); return} - guard let fh = FileHandle(forWritingAtPath: filePath) else { XCTAssertTrue(false, "Unable to open temporary file"); return } - defer { try! FileManager.default.removeItem(atPath: dirPath) } - - // Now use this data with some Objective-C code that takes NSData arguments - fh.write(data) - - // Get the data back - do { - let url = URL(fileURLWithPath: filePath) - let readData = try Data.init(contentsOf: url) - XCTAssertEqual(data.count, readData.count, "The length of the data is not the same") - } catch { - XCTAssertTrue(false, "Unable to read back data") - return - } - } - - func testEquality() { - let d1 = dataFrom("hello") - let d2 = dataFrom("hello") - - // Use == explicitly here to make sure we're calling the right methods - XCTAssertTrue(d1 == d2, "Data should be equal") - } - - func testDataInSet() { - let d1 = dataFrom("Hello") - let d2 = dataFrom("Hello") - let d3 = dataFrom("World") - - var s = Set() - s.insert(d1) - s.insert(d2) - s.insert(d3) - - XCTAssertEqual(s.count, 2, "Expected only two entries in the Set") - } - - func testReplaceSubrange() { - var hello = dataFrom("Hello") - let world = dataFrom("World") - - hello[0] = world[0] - XCTAssertEqual(hello[0], world[0]) - - var goodbyeWorld = dataFrom("Hello World") - let goodbye = dataFrom("Goodbye") - let expected = dataFrom("Goodbye World") - - goodbyeWorld.replaceSubrange(0..<5, with: goodbye) - XCTAssertEqual(goodbyeWorld, expected) - } - - func testReplaceSubrange2() { - let hello = dataFrom("Hello") - let world = dataFrom(" World") - let goodbye = dataFrom("Goodbye") - let expected = dataFrom("Goodbye World") - - var mutateMe = hello - mutateMe.append(world) - - if let found = mutateMe.range(of: hello) { - mutateMe.replaceSubrange(found, with: goodbye) - } - XCTAssertEqual(mutateMe, expected) - } - - func testReplaceSubrange3() { - // The expected result - let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] - let expected = expectedBytes.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - // The data we'll mutate - let someBytes : [UInt8] = [1, 2, 3, 4, 5] - var a = someBytes.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - // The bytes we'll insert - let b : [UInt8] = [9, 10, 11, 12, 13] - b.withUnsafeBufferPointer { - a.replaceSubrange(2..<5, with: $0) - } - XCTAssertEqual(expected, a) - } - - func testReplaceSubrange4() { - let expectedBytes : [UInt8] = [1, 2, 9, 10, 11, 12, 13] - let expected = Data(bytes: expectedBytes) - - // The data we'll mutate - let someBytes : [UInt8] = [1, 2, 3, 4, 5] - var a = Data(bytes: someBytes) - - // The bytes we'll insert - let b : [UInt8] = [9, 10, 11, 12, 13] - a.replaceSubrange(2..<5, with: b) - XCTAssertEqual(expected, a) - } - - func testReplaceSubrange5() { - var d = Data(bytes: [1, 2, 3]) - d.replaceSubrange(0..<0, with: [4]) - XCTAssertEqual(Data(bytes: [4, 1, 2, 3]), d) - - d.replaceSubrange(0..<4, with: [9]) - XCTAssertEqual(Data(bytes: [9]), d) - - d.replaceSubrange(0..= 65 && byte <= 90 } - - let allCaps = hello.filter(isCapital) - XCTAssertEqual(allCaps.count, 2) - - let capCount = hello.reduce(0) { isCapital($1) ? $0 + 1 : $0 } - XCTAssertEqual(capCount, 2) - - let allLower = hello.map { isCapital($0) ? $0 + 31 : $0 } - XCTAssertEqual(allLower.count, hello.count) - } - - func testCustomDeallocator() { - var deallocatorCalled = false - - // Scope the data to a block to control lifecycle - do { - let buffer = malloc(16)! - let bytePtr = buffer.bindMemory(to: UInt8.self, capacity: 16) - var data = Data(bytesNoCopy: bytePtr, count: 16, deallocator: .custom({ (ptr, size) in - deallocatorCalled = true - free(UnsafeMutableRawPointer(ptr)) - })) - // Use the data - data[0] = 1 - } - - XCTAssertTrue(deallocatorCalled, "Custom deallocator was never called") - } - - func testCopyBytes() { - let c = 10 - let underlyingBuffer = malloc(c * MemoryLayout.stride)! - let u16Ptr = underlyingBuffer.bindMemory(to: UInt16.self, capacity: c) - let buffer = UnsafeMutableBufferPointer(start: u16Ptr, count: c) - - buffer[0] = 0 - buffer[1] = 0 - - var data = Data(capacity: c * MemoryLayout.stride) - data.resetBytes(in: 0...stride) - data[0] = 0xFF - data[1] = 0xFF - let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(copiedCount, c * MemoryLayout.stride) - - XCTAssertEqual(buffer[0], 0xFFFF) - free(underlyingBuffer) - } - - func testCopyBytes_undersized() { - let a : [UInt8] = [1, 2, 3, 4, 5] - var data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - let expectedSize = MemoryLayout.stride * a.count - XCTAssertEqual(expectedSize, data.count) - - let underlyingBuffer = unsafeBitCast(malloc(expectedSize - 1)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize - 1) - - // We should only copy in enough bytes that can fit in the buffer - let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(expectedSize - 1, copiedCount) - - var index = 0 - for v in a[0...stride * a.count - XCTAssertEqual(expectedSize, data.count) - - let underlyingBuffer = unsafeBitCast(malloc(expectedSize + 1)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize + 1) - - let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(expectedSize, copiedCount) - - free(underlyingBuffer) - } - - func testCopyBytes_ranges() { - - do { - // Equal sized buffer, data - let a : [UInt8] = [1, 2, 3, 4, 5] - var data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - let underlyingBuffer = unsafeBitCast(malloc(data.count)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: data.count) - - var copiedCount : Int - - copiedCount = data.copyBytes(to: buffer, from: 0..<0) - XCTAssertEqual(0, copiedCount) - - copiedCount = data.copyBytes(to: buffer, from: 1..<1) - XCTAssertEqual(0, copiedCount) - - copiedCount = data.copyBytes(to: buffer, from: 0..<3) - XCTAssertEqual((0..<3).count, copiedCount) - - var index = 0 - for v in a[0..<3] { - XCTAssertEqual(v, buffer[index]) - index += 1 - } - free(underlyingBuffer) - } - - do { - // Larger buffer than data - let a : [UInt8] = [1, 2, 3, 4] - let data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - let underlyingBuffer = unsafeBitCast(malloc(10)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: 10) - - var copiedCount : Int - - copiedCount = data.copyBytes(to: buffer, from: 0..<3) - XCTAssertEqual((0..<3).count, copiedCount) - - var index = 0 - for v in a[0..<3] { - XCTAssertEqual(v, buffer[index]) - index += 1 - } - free(underlyingBuffer) - } - - do { - // Larger data than buffer - let a : [UInt8] = [1, 2, 3, 4, 5, 6] - let data = a.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - let underlyingBuffer = unsafeBitCast(malloc(4)!, to: UnsafeMutablePointer.self) - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: 4) - - var copiedCount : Int - - copiedCount = data.copyBytes(to: buffer, from: 0...stride * a.count - XCTAssertEqual(expectedSize, data.count) - - [false, true].withUnsafeBufferPointer { - data.append($0) - } - - expectedSize += MemoryLayout.stride * 2 - XCTAssertEqual(expectedSize, data.count) - - let underlyingBuffer = unsafeBitCast(malloc(expectedSize)!, to: UnsafeMutablePointer.self) - - let buffer = UnsafeMutableBufferPointer(start: underlyingBuffer, count: expectedSize) - let copiedCount = data.copyBytes(to: buffer) - XCTAssertEqual(copiedCount, expectedSize) - - free(underlyingBuffer) - } - - // intentionally structured so sizeof() != strideof() - struct MyStruct { - var time: UInt64 - let x: UInt32 - let y: UInt32 - let z: UInt32 - init() { - time = 0 - x = 1 - y = 2 - z = 3 - } - } - - func test_bufferSizeCalculation() { - // Make sure that Data is correctly using strideof instead of sizeof. - // n.b. if sizeof(MyStruct) == strideof(MyStruct), this test is not as useful as it could be - - // init - let stuff = [MyStruct(), MyStruct(), MyStruct()] - var data = stuff.withUnsafeBufferPointer { - return Data(buffer: $0) - } - - XCTAssertEqual(data.count, MemoryLayout.stride * 3) - - - // append - stuff.withUnsafeBufferPointer { - data.append($0) - } - - XCTAssertEqual(data.count, MemoryLayout.stride * 6) - - // copyBytes - do { - // equal size - let underlyingBuffer = malloc(6 * MemoryLayout.stride)! - defer { free(underlyingBuffer) } - - let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 6) - let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) - - let byteCount = data.copyBytes(to: buffer) - XCTAssertEqual(6 * MemoryLayout.stride, byteCount) - } - - do { - // undersized - let underlyingBuffer = malloc(3 * MemoryLayout.stride)! - defer { free(underlyingBuffer) } - - let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 3) - let buffer = UnsafeMutableBufferPointer(start: ptr, count: 3) - - let byteCount = data.copyBytes(to: buffer) - XCTAssertEqual(3 * MemoryLayout.stride, byteCount) - } - - do { - // oversized - let underlyingBuffer = malloc(12 * MemoryLayout.stride)! - defer { free(underlyingBuffer) } - - let ptr = underlyingBuffer.bindMemory(to: MyStruct.self, capacity: 6) - let buffer = UnsafeMutableBufferPointer(start: ptr, count: 6) - - let byteCount = data.copyBytes(to: buffer) - XCTAssertEqual(6 * MemoryLayout.stride, byteCount) - } - } -} - diff --git a/TestFoundation/TestNSJSONSerialization.swift b/TestFoundation/TestNSJSONSerialization.swift index eca770796a..8950515c2e 100644 --- a/TestFoundation/TestNSJSONSerialization.swift +++ b/TestFoundation/TestNSJSONSerialization.swift @@ -45,10 +45,7 @@ extension TestNSJSONSerialization { } func test_JSONObjectWithData_emptyObject() { - var bytes: [UInt8] = [0x7B, 0x7D] - let subject = bytes.withUnsafeMutableBufferPointer { - return Data(buffer: $0) - } + let subject = Data(bytes: UnsafeRawPointer([UInt8]([0x7B, 0x7D])), count: 2) let object = try! JSONSerialization.jsonObject(with: subject, options: []) as? [String:Any] XCTAssertEqual(object?.count, 0) @@ -78,7 +75,7 @@ extension TestNSJSONSerialization { ] for (description, encoded) in subjects { - let result = try? JSONSerialization.jsonObject(with: Data(bytes:encoded, count: encoded.count), options: []) + let result = try? JSONSerialization.jsonObject(with: Data(bytes:UnsafeRawPointer(encoded), count: encoded.count), options: []) XCTAssertNotNil(result, description) } } diff --git a/TestFoundation/TestNSXMLParser.swift b/TestFoundation/TestNSXMLParser.swift index 486f23dd92..239d27c100 100644 --- a/TestFoundation/TestNSXMLParser.swift +++ b/TestFoundation/TestNSXMLParser.swift @@ -28,9 +28,7 @@ class TestNSXMLParser : XCTestCase { func test_data() { let xml = Array("bar".utf8CString) let data = xml.withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> Data in - return buffer.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: buffer.count * MemoryLayout.stride) { - return Data(bytes: $0, count: buffer.count) - } + return Data(bytes: UnsafeRawPointer(buffer.baseAddress!), count: buffer.count) } let parser = XMLParser(data: data) let res = parser.parse()