Skip to content

Commit bdafb2d

Browse files
committed
Handle no-copy cases and custom deallocators as inline construction of swift Data
1 parent 9c0951b commit bdafb2d

File tree

2 files changed

+71
-8
lines changed

2 files changed

+71
-8
lines changed

stdlib/public/SDK/Foundation/Data.swift

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public final class _DataStorage {
8181
public var _length: Int
8282
public var _capacity: Int
8383
public var _needToZero: Bool
84+
public var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? = nil
8485
public var _backing: Backing = .swift
8586

8687
public var bytes: UnsafeRawPointer? {
@@ -152,11 +153,14 @@ public final class _DataStorage {
152153

153154
public func _freeBytes() {
154155
if let bytes = _bytes {
155-
free(bytes)
156+
if let dealloc = _deallocator {
157+
dealloc(bytes, length)
158+
} else {
159+
free(bytes)
160+
}
156161
}
157162
}
158163

159-
160164
public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer<UInt8>, _ byteIndex: Data.Index, _ stop: inout Bool) -> Void) {
161165
var stop: Bool = false
162166
switch _backing {
@@ -222,7 +226,16 @@ public final class _DataStorage {
222226
/* Where calloc/memmove/free fails, realloc might succeed */
223227
if newBytes == nil {
224228
allocateCleared = false
225-
newBytes = realloc(_bytes!, newCapacity)
229+
if _deallocator != nil {
230+
newBytes = _DataStorage.allocate(newCapacity, true)
231+
if newBytes != nil {
232+
_DataStorage.move(newBytes!, _bytes!, origLength)
233+
_freeBytes()
234+
_deallocator = nil
235+
}
236+
} else {
237+
newBytes = realloc(_bytes!, newCapacity)
238+
}
226239
}
227240

228241
/* Try again with minimum length */
@@ -571,16 +584,46 @@ public final class _DataStorage {
571584
}
572585

573586

574-
public convenience init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) {
587+
public init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) {
575588
precondition(length < _DataStorage.maxSize)
576-
self.init(capacity: length)
577-
_DataStorage.move(_bytes!, bytes, length)
578-
if length > 0 {
589+
if length == 0 {
590+
_capacity = 0
591+
_length = 0
592+
_needToZero = false
593+
_bytes = nil
594+
if let dealloc = deallocator,
595+
let bytes_ = bytes {
596+
dealloc(bytes_, length)
597+
}
598+
} else if !copy {
599+
_capacity = length
600+
_length = length
601+
_needToZero = false
602+
_bytes = bytes
603+
_deallocator = deallocator
604+
} else if _DataStorage.vmOpsThreshold <= length {
605+
_capacity = length
606+
_length = length
607+
_needToZero = true
608+
_bytes = _DataStorage.allocate(length, false)!
609+
_DataStorage.move(_bytes!, bytes, length)
610+
if let dealloc = deallocator {
611+
dealloc(bytes!, length)
612+
}
613+
} else {
614+
var capacity = length
615+
if (_DataStorage.vmOpsThreshold <= capacity) {
616+
capacity = NSRoundUpToMultipleOfPageSize(capacity)
617+
}
618+
_length = length
619+
_bytes = _DataStorage.allocate(capacity, false)!
620+
_capacity = capacity
621+
_needToZero = true
622+
_DataStorage.move(_bytes!, bytes, length)
579623
if let dealloc = deallocator {
580624
dealloc(bytes!, length)
581625
}
582626
}
583-
_length = length
584627
}
585628

586629
public init(immutableReference: NSData) {

test/stdlib/TestData.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,25 @@ class TestData : TestDataSuper {
879879
expectNotEqual(anyHashables[0], anyHashables[1])
880880
expectEqual(anyHashables[1], anyHashables[2])
881881
}
882+
883+
func test_noCopyBehavior() {
884+
let ptr = UnsafeMutableRawPointer(bitPattern: 0x1)!
885+
886+
var deallocated = false
887+
autoreleasepool {
888+
let data = Data(bytesNoCopy: ptr, count: 1, deallocator: .custom({ (bytes, length) in
889+
deallocated = true
890+
}))
891+
expectFalse(deallocated)
892+
let equal = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Bool in
893+
return ptr == UnsafeMutableRawPointer(mutating: bytes)
894+
}
895+
896+
expectTrue(equal)
897+
}
898+
899+
expectTrue(deallocated)
900+
}
882901
}
883902

884903
#if !FOUNDATION_XCTEST
@@ -921,6 +940,7 @@ DataTests.test("test_bufferSizeCalculation") { TestData().test_bufferSizeCalcula
921940
DataTests.test("test_classForCoder") { TestData().test_classForCoder() }
922941
DataTests.test("test_AnyHashableContainingData") { TestData().test_AnyHashableContainingData() }
923942
DataTests.test("test_AnyHashableCreatedFromNSData") { TestData().test_AnyHashableCreatedFromNSData() }
943+
DataTests.test("test_noCopyBehavior") { TestData().test_noCopyBehavior() }
924944

925945
// XCTest does not have a crash detection, whereas lit does
926946
DataTests.test("bounding failure subdata") {

0 commit comments

Comments
 (0)