Skip to content

Commit 1e9db60

Browse files
committed
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).
1 parent 5871161 commit 1e9db60

11 files changed

+1111
-437
lines changed

Foundation/Data.swift

Lines changed: 111 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -73,56 +73,15 @@ internal final class _SwiftNSData : NSData, _SwiftNativeFoundationType {
7373
releaseWrappedObject()
7474
}
7575

76-
// Stubs
77-
// -----
78-
76+
// MARK: - Funnel overrides
7977
override var length : Int {
8078
get {
8179
return _mapUnmanaged { $0.length }
8280
}
8381
}
84-
8582
override var bytes : UnsafeRawPointer {
8683
return _mapUnmanaged { $0.bytes }
8784
}
88-
89-
// override func subdata(with range: NSRange) -> Data {
90-
// return _mapUnmanaged { $0.subdata(with: range) }
91-
// }
92-
//
93-
// override func getBytes(_ buffer: UnsafeMutableRawPointer, length: Int) {
94-
// return _mapUnmanaged { $0.getBytes(buffer, length: length) }
95-
// }
96-
//
97-
// override func getBytes(_ buffer: UnsafeMutableRawPointer, range: NSRange) {
98-
// return _mapUnmanaged { $0.getBytes(buffer, range: range) }
99-
// }
100-
//
101-
// override func isEqual(to other: Data) -> Bool {
102-
// return _mapUnmanaged { return $0.isEqual(to: other) }
103-
// }
104-
//
105-
// override func write(to url: URL, options: Data.WritingOptions) throws {
106-
// return try _mapUnmanaged { try $0.write(to: url, options: options) }
107-
// }
108-
//
109-
// override func range(of data: Data, options: Data.SearchOptions, range: NSRange) -> NSRange {
110-
// return _mapUnmanaged {
111-
// $0.range(of: data, options: options, in: range)
112-
// }
113-
// }
114-
//
115-
// override func enumerateByteRanges(using block: (UnsafeRawPointer, NSRange, UnsafeMutablePointer<ObjCBool>) -> Void) {
116-
// return _mapUnmanaged { $0.enumerateBytes(block) }
117-
// }
118-
//
119-
// override func base64EncodedString(options: Data.Base64EncodingOptions) -> String {
120-
// return _mapUnmanaged { $0.base64EncodedString(options) }
121-
// }
122-
//
123-
// override func base64EncodedData(options: Data.Base64EncodingOptions) -> Data {
124-
// return _mapUnmanaged { $0.base64EncodedData(options) }
125-
// }
12685
}
12786

12887
/**
@@ -197,10 +156,17 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
197156
_wrapped = _SwiftNSData(immutableObject: NSData(bytes: buffer.baseAddress, length: MemoryLayout<SourceType>.stride * buffer.count))
198157
}
199158

159+
/// Initialize a `Data` with copied memory content.
160+
///
161+
/// - parameter buffer: A buffer pointer to copy. The size is calculated from `SourceType` and `buffer.count`.
162+
public init<SourceType>(buffer: UnsafeMutableBufferPointer<SourceType>) {
163+
_wrapped = _SwiftNSData(immutableObject: NSData(bytes: UnsafePointer(buffer.baseAddress), length: MemoryLayout<SourceType>.stride * buffer.count))
164+
}
165+
200166
/// Initialize a `Data` with the contents of an Array.
201167
///
202168
/// - parameter bytes: An array of bytes to copy.
203-
public init(bytes: Array<UInt8>) {
169+
public init(bytes: [UInt8]) {
204170
_wrapped = bytes.withUnsafeBufferPointer {
205171
return _SwiftNSData(immutableObject: NSData(bytes: $0.baseAddress, length: $0.count))
206172
}
@@ -226,7 +192,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
226192
/// - parameter capacity: The size of the data.
227193
public init(capacity: Int) {
228194
if let d = NSMutableData(capacity: capacity) {
229-
_wrapped = _SwiftNSData(immutableObject: d)
195+
_wrapped = _SwiftNSData(mutableObject: d)
230196
} else {
231197
fatalError("Unable to allocate data of the requested capacity")
232198
}
@@ -261,7 +227,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
261227
///
262228
/// Returns nil when the input is not recognized as valid Base-64.
263229
/// - parameter base64String: The string to parse.
264-
/// - parameter options: Decoding options. Default value is `[]`.
230+
/// - parameter options: Encoding options. Default value is `[]`.
265231
public init?(base64Encoded base64String: String, options: Data.Base64DecodingOptions = []) {
266232
if let d = NSData(base64Encoded: base64String, options: Base64DecodingOptions(rawValue: options.rawValue)) {
267233
_wrapped = _SwiftNSData(immutableObject: d)
@@ -288,16 +254,23 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
288254
///
289255
/// - parameter count: The number of bytes the data initially contains.
290256
public init(count: Int) {
291-
if let memory = calloc(1, count)?.bindMemory(to: UInt8.self, capacity: count) {
292-
self.init(bytesNoCopy: memory, count: count, deallocator: .free)
257+
if let d = NSMutableData(length: count) {
258+
_wrapped = _SwiftNSData(mutableObject: d)
293259
} else {
294260
fatalError("Unable to allocate data of the requested count")
295261
}
296262
}
263+
297264

298-
internal init(_bridged data: NSData) {
299-
// We must copy the input because it might be mutable; just like storing a value type in ObjC
300-
_wrapped = _SwiftNSData(immutableObject: data.copy() as! NSObject)
265+
/// Initialize a `Data` by adopting a reference type.
266+
///
267+
/// 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.
268+
///
269+
/// 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.
270+
///
271+
/// - parameter reference: The instance of `NSData` that you wish to wrap. This instance will be copied by `struct Data`.
272+
public init(referencing reference: NSData) {
273+
_wrapped = _SwiftNSData(immutableObject: reference.copy() as! AnyObject)
301274
}
302275

303276
// -----------------------------------
@@ -356,7 +329,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
356329
_mapUnmanaged { $0.getBytes(pointer, length: count) }
357330
}
358331

359-
private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: NSRange) {
332+
private func _copyBytesHelper(to pointer: UnsafeMutablePointer<UInt8>, from range: NSRange) {
360333
_mapUnmanaged { $0.getBytes(pointer, range: range) }
361334
}
362335

@@ -366,7 +339,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
366339
/// - parameter range: The range in the `Data` to copy.
367340
/// - warning: This method does not verify that the contents at pointer have enough space to hold the required number of bytes.
368341
public func copyBytes(to pointer: UnsafeMutablePointer<UInt8>, from range: Range<Index>) {
369-
_copyBytesHelper(to: pointer, from: NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound))
342+
_copyBytesHelper(to: pointer, from: NSRange(range))
370343
}
371344

372345
/// Copy the contents of the data into a buffer.
@@ -397,8 +370,10 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
397370
guard !copyRange.isEmpty else { return 0 }
398371

399372
let nsRange = NSMakeRange(copyRange.lowerBound, copyRange.upperBound - copyRange.lowerBound)
400-
let pointer = UnsafeMutableRawPointer(buffer.baseAddress!)
401-
_copyBytesHelper(to: pointer, from: nsRange)
373+
let ptr = buffer.baseAddress!
374+
ptr.withMemoryRebound(to: UInt8.self, capacity: buffer.count) {
375+
_copyBytesHelper(to: $0, from: nsRange)
376+
}
402377
return copyRange.count
403378
}
404379

@@ -499,10 +474,13 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
499474

500475
/// Replace a region of bytes in the data with new data.
501476
///
502-
/// - parameter range: The range in the data to replace.
477+
/// This will resize the data if required, to fit the entire contents of `data`.
478+
///
479+
/// - precondition: The bounds of `subrange` must be valid indices of the collection.
480+
/// - parameter subrange: The range in the data to replace. If `subrange.lowerBound == data.count && subrange.count == 0` then this operation is an append.
503481
/// - parameter data: The replacement data.
504-
public mutating func replaceBytes(in range: Range<Index>, with data: Data) {
505-
let nsRange = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound)
482+
public mutating func replaceSubrange(_ subrange: Range<Index>, with data: Data) {
483+
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
506484
let cnt = data.count
507485
let bytes = data._getUnsafeBytesPointer()
508486

@@ -511,6 +489,68 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
511489
}
512490
}
513491

492+
/// Replace a region of bytes in the data with new bytes from a buffer.
493+
///
494+
/// This will resize the data if required, to fit the entire contents of `buffer`.
495+
///
496+
/// - precondition: The bounds of `subrange` must be valid indices of the collection.
497+
/// - parameter subrange: The range in the data to replace.
498+
/// - parameter buffer: The replacement bytes.
499+
public mutating func replaceSubrange<SourceType>(_ subrange: Range<Index>, with buffer: UnsafeBufferPointer<SourceType>) {
500+
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
501+
let bufferCount = buffer.count * MemoryLayout<SourceType>.stride
502+
503+
_applyUnmanagedMutation {
504+
$0.replaceBytes(in: nsRange, withBytes: buffer.baseAddress!, length: bufferCount)
505+
}
506+
507+
}
508+
509+
/// Replace a region of bytes in the data with new bytes from a collection.
510+
///
511+
/// This will resize the data if required, to fit the entire contents of `newElements`.
512+
///
513+
/// - precondition: The bounds of `subrange` must be valid indices of the collection.
514+
/// - parameter subrange: The range in the data to replace.
515+
/// - parameter newElements: The replacement bytes.
516+
public mutating func replaceSubrange<ByteCollection : Collection>(_ subrange: Range<Index>, with newElements: ByteCollection) where ByteCollection.Iterator.Element == Data.Iterator.Element {
517+
518+
// Calculate this once, it may not be O(1)
519+
let replacementCount : Int = numericCast(newElements.count)
520+
let currentCount = self.count
521+
let subrangeCount = subrange.count
522+
523+
if currentCount < subrange.lowerBound + subrangeCount {
524+
if subrangeCount == 0 {
525+
preconditionFailure("location \(subrange.lowerBound) exceeds data count \(currentCount)")
526+
} else {
527+
preconditionFailure("range \(subrange) exceeds data count \(currentCount)")
528+
}
529+
}
530+
531+
let resultCount = currentCount - subrangeCount + replacementCount
532+
if resultCount != currentCount {
533+
// This may realloc.
534+
// 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.
535+
self.count = resultCount
536+
}
537+
538+
let shift = resultCount - currentCount
539+
let start = subrange.lowerBound
540+
541+
self.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer<UInt8>) -> () in
542+
if shift != 0 {
543+
let destination = bytes + start + replacementCount
544+
let source = bytes + start + subrangeCount
545+
memmove(destination, source, currentCount - start - subrangeCount)
546+
}
547+
548+
if replacementCount != 0 {
549+
newElements._copyContents(initializing: bytes + start)
550+
}
551+
}
552+
}
553+
514554
/// Return a new copy of the data in a specified range.
515555
///
516556
/// - parameter range: The range to copy.
@@ -526,16 +566,16 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
526566
///
527567
/// - parameter options: The options to use for the encoding. Default value is `[]`.
528568
/// - returns: The Base-64 encoded string.
529-
public func base64EncodedString(_ options: Data.Base64EncodingOptions = []) -> String {
530-
return _mapUnmanaged { $0.base64EncodedString(options) }
569+
public func base64EncodedString(options: Data.Base64EncodingOptions = []) -> String {
570+
return _mapUnmanaged { $0.base64EncodedString(options: options) }
531571
}
532572

533573
/// Returns a Base-64 encoded `Data`.
534574
///
535575
/// - parameter options: The options to use for the encoding. Default value is `[]`.
536576
/// - returns: The Base-64 encoded data.
537-
public func base64EncodedData(_ options: Data.Base64EncodingOptions = []) -> Data {
538-
return _mapUnmanaged { $0.base64EncodedData(options) }
577+
public func base64EncodedData(options: Data.Base64EncodingOptions = []) -> Data {
578+
return _mapUnmanaged { $0.base64EncodedData(options: options) }
539579
}
540580

541581
// MARK: -
@@ -556,7 +596,6 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
556596
return _mapUnmanaged { $0.debugDescription }
557597
}
558598

559-
// MARK: -
560599

561600
// MARK: -
562601
// MARK: Index and Subscript
@@ -577,20 +616,12 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
577616
}
578617
}
579618

580-
public subscript(bounds: Range<Int>) -> MutableRandomAccessSlice<Data> {
619+
public subscript(bounds: Range<Index>) -> MutableRandomAccessSlice<Data> {
581620
get {
582621
return MutableRandomAccessSlice(base: self, bounds: bounds)
583622
}
584623
set {
585-
// Ideally this would be:
586-
// replaceBytes(in: bounds, with: newValue._base)
587-
// but we do not have access to _base due to 'internal' protection
588-
// TODO: Use a custom Slice type so we have access to the underlying data
589-
let arrayOfBytes = newValue.map { $0 }
590-
arrayOfBytes.withUnsafeBufferPointer {
591-
let otherData = Data(buffer: $0)
592-
replaceBytes(in: bounds, with: otherData)
593-
}
624+
replaceSubrange(bounds, with: newValue.base)
594625
}
595626
}
596627

@@ -620,12 +651,14 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
620651
public func makeIterator() -> Data.Iterator {
621652
return IndexingIterator(_elements: self)
622653
}
654+
655+
/// Returns `true` if the two `Data` arguments are equal.
656+
public static func ==(d1 : Data, d2 : Data) -> Bool {
657+
return d1._wrapped.isEqual(to: d2)
658+
}
623659
}
624660

625-
/// Returns `true` if the two `Data` arguments are equal.
626-
public func ==(d1 : Data, d2 : Data) -> Bool {
627-
return d1._wrapped.isEqual(to: d2)
628-
}
661+
629662

630663
/// Provides bridging functionality for struct Data to class NSData and vice-versa.
631664
extension Data : _ObjectTypeBridgeable {
@@ -639,11 +672,11 @@ extension Data : _ObjectTypeBridgeable {
639672
}
640673

641674
public static func _forceBridgeFromObjectiveC(_ input: NSData, result: inout Data?) {
642-
result = Data(_bridged: input)
675+
result = Data(referencing: input)
643676
}
644677

645678
public static func _conditionallyBridgeFromObjectiveC(_ input: NSData, result: inout Data?) -> Bool {
646-
result = Data(_bridged: input)
679+
result = Data(referencing: input)
647680
return true
648681
}
649682

Foundation/NSConcreteValue.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ internal class NSConcreteValue : NSValue {
113113
}
114114

115115
override var description : String {
116-
return Data(bytes: self.value, count: self._size).description
116+
let boundBytes = self.value.bindMemory(to: UInt8.self, capacity: self._size)
117+
return Data(bytes: boundBytes, count: self._size).description
117118
}
118119

119120
convenience required init?(coder aDecoder: NSCoder) {

0 commit comments

Comments
 (0)