From 1423298b0802a1d774f770668940601e06286120 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Wed, 27 Dec 2017 18:26:41 +0000 Subject: [PATCH] NSMutableData: Restore missing initialisers - When aligning the init() methods with Darwin in PR #1287, converting the NSData `convenience` methods to designated initialisers stopped them from being inherited by NSMutableData as it declared its own designed initialisers. - Add in all of the missing initialisers and add tests to ensure their presence is checked at compile time. - Fix init(data: Data) to use the correct length of the data argument when copying the bytes. --- Foundation/NSData.swift | 45 +++++++++++- TestFoundation/TestNSData.swift | 117 ++++++++++++++++++++++++++++++-- 2 files changed, 156 insertions(+), 6 deletions(-) diff --git a/Foundation/NSData.swift b/Foundation/NSData.swift index d41697c7fd..387245b79d 100644 --- a/Foundation/NSData.swift +++ b/Foundation/NSData.swift @@ -180,7 +180,7 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding { /// Initializes a data object with the contents of another data object. public init(data: Data) { super.init() - _init(bytes: UnsafeMutableRawPointer(mutating: data._nsObject.bytes), length: length, copy: true) + _init(bytes: UnsafeMutableRawPointer(mutating: data._nsObject.bytes), length: data.count, copy: true) } /// Initializes a data object with the data from the location specified by a given URL. @@ -966,7 +966,7 @@ open class NSMutableData : NSData { } // NOTE: the deallocator block here is implicitly @escaping by virtue of it being optional - public override init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool = false, deallocator: (/*@escaping*/ (UnsafeMutableRawPointer, Int) -> Void)? = nil) { + fileprivate 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) } @@ -990,6 +990,47 @@ open class NSMutableData : NSData { super.init(coder: aDecoder) } + public override init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int) { + super.init(bytesNoCopy: bytes, length: length) + } + + public override init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? = nil) { + super.init(bytesNoCopy: bytes, length: length, deallocator: deallocator) + } + + public override init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, freeWhenDone: Bool) { + super.init(bytesNoCopy: bytes, length: length, freeWhenDone: freeWhenDone) + } + + public override init(data: Data) { + super.init(data: data) + } + + public override init?(contentsOfFile path: String) { + super.init(contentsOfFile: path) + } + + public override init(contentsOfFile path: String, options: NSData.ReadingOptions = []) throws { + try super.init(contentsOfFile: path, options: options) + } + + public override init?(contentsOf url: URL) { + super.init(contentsOf: url) + } + + public override init(contentsOf url: URL, options: NSData.ReadingOptions = []) throws { + try super.init(contentsOf: url, options: options) + } + + public override init?(base64Encoded base64Data: Data, options: NSData.Base64DecodingOptions = []) { + super.init(base64Encoded: base64Data, options: options) + } + + public override init?(base64Encoded base64Data: String, options: NSData.Base64DecodingOptions = []) { + super.init(base64Encoded: base64Data, options: options) + } + + // MARK: - Funnel Methods /// A pointer to the data contained by the mutable data object. open var mutableBytes: UnsafeMutableRawPointer { diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift index 61be84b72a..82f87b99b4 100644 --- a/TestFoundation/TestNSData.swift +++ b/TestFoundation/TestNSData.swift @@ -206,7 +206,6 @@ class TestNSData: XCTestCase { ("test_dataHash", test_dataHash), ("test_genericBuffers", test_genericBuffers), ("test_writeFailure", test_writeFailure), - ("testBasicConstruction", testBasicConstruction), ("testBridgingDefault", testBridgingDefault), ("testBridgingMutable", testBridgingMutable), ("testCopyBytes_oversized", testCopyBytes_oversized), @@ -248,8 +247,14 @@ class TestNSData: XCTestCase { ("test_initializeWithBase64EncodedStringGetsDecodedData", test_initializeWithBase64EncodedStringGetsDecodedData), ("test_base64DecodeWithPadding1", test_base64DecodeWithPadding1), ("test_base64DecodeWithPadding2", test_base64DecodeWithPadding2), - ("test_rangeOfData",test_rangeOfData), - ("test_initMutableDataWithLength", test_initMutableDataWithLength), + ("test_rangeOfData", test_rangeOfData), + ("test_initNSMutableData()", test_initNSMutableData), + ("test_initNSMutableDataWithLength", test_initNSMutableDataWithLength), + ("test_initNSMutableDataWithCapacity", test_initNSMutableDataWithCapacity), + ("test_initNSMutableDataFromData", test_initNSMutableDataFromData), + ("test_initNSMutableDataFromBytes", test_initNSMutableDataFromBytes), + ("test_initNSMutableDataContentsOf", test_initNSMutableDataContentsOf), + ("test_initNSMutableDataBase64", test_initNSMutableDataBase64), ("test_replaceBytes", test_replaceBytes), ("test_replaceBytesWithNil", test_replaceBytesWithNil), ("test_initDataWithCapacity", test_initDataWithCapacity), @@ -811,12 +816,116 @@ class TestNSData: XCTestCase { } - func test_initMutableDataWithLength() { + // Check all of the NSMutableData constructors are available. + func test_initNSMutableData() { + let mData = NSMutableData() + XCTAssertNotNil(mData) + XCTAssertEqual(mData.length, 0) + } + + func test_initNSMutableDataWithLength() { let mData = NSMutableData(length: 30) XCTAssertNotNil(mData) XCTAssertEqual(mData!.length, 30) } + func test_initNSMutableDataWithCapacity() { + let mData = NSMutableData(capacity: 30) + XCTAssertNotNil(mData) + XCTAssertEqual(mData!.length, 0) + } + + func test_initNSMutableDataFromData() { + let data = Data(bytes: [1, 2, 3]) + let mData = NSMutableData(data: data) + XCTAssertEqual(mData.length, 3) + XCTAssertEqual(NSData(data: data), mData) + } + + func test_initNSMutableDataFromBytes() { + let data = Data([1, 2, 3, 4, 5, 6]) + var testBytes: [UInt8] = [1, 2, 3, 4, 5, 6] + + let md1 = NSMutableData(bytes: &testBytes, length: testBytes.count) + XCTAssertEqual(md1, NSData(data: data)) + + let md2 = NSMutableData(bytes: nil, length: 0) + XCTAssertEqual(md2.length, 0) + + let testBuffer = malloc(testBytes.count)! + let md3 = NSMutableData(bytesNoCopy: testBuffer, length: testBytes.count) + md3.replaceBytes(in: NSRange(location: 0, length: testBytes.count), withBytes: &testBytes) + XCTAssertEqual(md3, NSData(data: data)) + + let md4 = NSMutableData(bytesNoCopy: &testBytes, length: testBytes.count, deallocator: nil) + XCTAssertEqual(md4.length, testBytes.count) + + let md5 = NSMutableData(bytesNoCopy: &testBytes, length: testBytes.count, freeWhenDone: false) + XCTAssertEqual(md5, NSData(data: data)) + } + + func test_initNSMutableDataContentsOf() { + let testDir = testBundle().resourcePath + let filename = testDir!.appending("/NSStringTestData.txt") + let url = URL(fileURLWithPath: filename) + + func testText(_ mData: NSMutableData?) { + guard let mData = mData else { + XCTFail("Contents of file are Nil") + return + } + if let txt = String(data: Data(referencing: mData), encoding: .ascii) { + XCTAssertEqual(txt, "swift-corelibs-foundation") + } else { + XCTFail("Cant convert to string") + } + } + + let contents1 = NSMutableData(contentsOfFile: filename) + XCTAssertNotNil(contents1) + testText(contents1) + + let contents2 = try? NSMutableData(contentsOfFile: filename, options: []) + XCTAssertNotNil(contents2) + testText(contents2) + + let contents3 = NSMutableData(contentsOf: url) + XCTAssertNotNil(contents3) + testText(contents3) + + let contents4 = try? NSMutableData(contentsOf: url, options: []) + XCTAssertNotNil(contents4) + testText(contents4) + + // Test failure to read + let badFilename = "does not exist" + let badUrl = URL(fileURLWithPath: badFilename) + + XCTAssertNil(NSMutableData(contentsOfFile: badFilename)) + XCTAssertNil(try? NSMutableData(contentsOfFile: badFilename, options: [])) + XCTAssertNil(NSMutableData(contentsOf: badUrl)) + XCTAssertNil(try? NSMutableData(contentsOf: badUrl, options: [])) + } + + func test_initNSMutableDataBase64() { + let srcData = Data([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) + let base64Data = srcData.base64EncodedData() + let base64String = srcData.base64EncodedString() + XCTAssertEqual(base64String, "AQIDBAUGBwgJAA==") + + let mData1 = NSMutableData(base64Encoded: base64Data) + XCTAssertNotNil(mData1) + XCTAssertEqual(mData1!, NSData(data: srcData)) + + let mData2 = NSMutableData(base64Encoded: base64String) + XCTAssertNotNil(mData2) + XCTAssertEqual(mData2!, NSData(data: srcData)) + + // Test bad input + XCTAssertNil(NSMutableData(base64Encoded: Data([1,2,3]), options: [])) + XCTAssertNil(NSMutableData(base64Encoded: "x", options: [])) + } + func test_replaceBytes() { var data = Data(bytes: [0, 0, 0, 0, 0]) let newData = Data(bytes: [1, 2, 3, 4, 5])