Data slice mutation support (#11939)

* Mutations of slices of data should preserve relative indexing as well as cow semantics of slices

* Ensure hashes of ranges are uniform to the expected hash for Data

* Correct a few mistakes in the slice mutation tests

* Update sequence initializations to avoid directly calling mutableCopy which prevents slice offset mismatches

* Avoid invalid index slices in creating mirrors

* Restore the original Data description

* Resetting a slice region should expand the slice to the maximum of the region (not a out of bounds index of the backing buffer)

* Remove stray comment and use a stack buffer for sequence appending

* Return false when allocations fail in _resizeConditionalAllocationBuffer (not yet in use)

* Enumeration of regions of a slice should be limited to the slice range in the case of custom backing (e.g. dispatch_data_t)

* adjust assertion warnings for data indexes that are negative
This commit is contained in:
Philippe Hausler
2017-09-16 13:22:01 -07:00
committed by GitHub
parent 74f02ad16b
commit c8bbce6ef1
4 changed files with 3028 additions and 195 deletions

View File

@@ -27,6 +27,10 @@ internal func __NSDataInvokeDeallocatorUnmap(_ mem: UnsafeMutableRawPointer, _ l
internal func __NSDataInvokeDeallocatorFree(_ mem: UnsafeMutableRawPointer, _ length: Int) {
free(mem)
}
internal func __NSDataIsCompact(_ data: NSData) -> Bool {
return data._isCompact()
}
#else
@@ -98,47 +102,140 @@ public final class _DataStorage {
public var _needToZero: Bool
public var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?
public var _backing: Backing = .swift
public var _offset: Int
public var bytes: UnsafeRawPointer? {
@inline(__always)
get {
switch _backing {
case .swift:
return UnsafeRawPointer(_bytes)
return UnsafeRawPointer(_bytes)?.advanced(by: -_offset)
case .immutable:
return UnsafeRawPointer(_bytes)
return UnsafeRawPointer(_bytes)?.advanced(by: -_offset)
case .mutable:
return UnsafeRawPointer(_bytes)
return UnsafeRawPointer(_bytes)?.advanced(by: -_offset)
case .customReference(let d):
return d.bytes
return d.bytes.advanced(by: -_offset)
case .customMutableReference(let d):
return d.bytes
return d.bytes.advanced(by: -_offset)
}
}
}
@discardableResult
public func withUnsafeBytes<Result>(in range: Range<Int>, apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result {
switch _backing {
case .swift: fallthrough
case .immutable: fallthrough
case .mutable:
return try apply(UnsafeRawBufferPointer(start: _bytes?.advanced(by: range.lowerBound - _offset), count: Swift.min(range.count, _length)))
case .customReference(let d):
if d._isCompact() {
let len = d.length
guard len > 0 else {
return try apply(UnsafeRawBufferPointer(start: nil, count: 0))
}
return try apply(UnsafeRawBufferPointer(start: d.bytes.advanced(by: range.lowerBound - _offset), count: Swift.min(range.count, len)))
} else {
var buffer = UnsafeMutableRawBufferPointer.allocate(count: range.count)
defer { buffer.deallocate() }
let sliceRange = NSRange(location: range.lowerBound - _offset, length: range.count)
var enumerated = 0
d.enumerateBytes { (ptr, byteRange, stop) in
if NSIntersectionRange(sliceRange, byteRange).length > 0 {
let lower = Swift.max(byteRange.location, sliceRange.location)
let upper = Swift.min(byteRange.location + byteRange.length, sliceRange.location + sliceRange.length)
let offset = lower - byteRange.location
let effectiveRange = NSRange(location: lower, length: upper - lower)
if effectiveRange == sliceRange {
memcpy(buffer.baseAddress!, ptr, effectiveRange.length)
stop.pointee = true
} else {
memcpy(buffer.baseAddress!.advanced(by: enumerated), ptr, effectiveRange.length)
}
enumerated += byteRange.length
} else if sliceRange.location + sliceRange.length < byteRange.location {
stop.pointee = true
}
}
return try apply(UnsafeRawBufferPointer(buffer))
}
case .customMutableReference(let d):
if d._isCompact() {
let len = d.length
guard len > 0 else {
return try apply(UnsafeRawBufferPointer(start: nil, count: 0))
}
return try apply(UnsafeRawBufferPointer(start: d.bytes.advanced(by: range.lowerBound - _offset), count: Swift.min(range.count, len)))
} else {
var buffer = UnsafeMutableRawBufferPointer.allocate(count: range.count)
defer { buffer.deallocate() }
let sliceRange = NSRange(location: range.lowerBound - _offset, length: range.count)
var enumerated = 0
d.enumerateBytes { (ptr, byteRange, stop) in
if NSIntersectionRange(sliceRange, byteRange).length > 0 {
let lower = Swift.max(byteRange.location, sliceRange.location)
let upper = Swift.min(byteRange.location + byteRange.length, sliceRange.location + sliceRange.length)
let effectiveRange = NSRange(location: lower, length: upper - lower)
if effectiveRange == sliceRange {
memcpy(buffer.baseAddress!, ptr, effectiveRange.length)
stop.pointee = true
} else {
memcpy(buffer.baseAddress!.advanced(by: enumerated), ptr, effectiveRange.length)
}
enumerated += byteRange.length
} else if sliceRange.location + sliceRange.length < byteRange.location {
stop.pointee = true
}
}
return try apply(UnsafeRawBufferPointer(buffer))
}
}
}
@discardableResult
public func withUnsafeMutableBytes<Result>(in range: Range<Int>, apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result {
switch _backing {
case .swift: fallthrough
case .mutable:
return try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.count, _length - range.lowerBound)))
case .customMutableReference(let d):
let len = d.length
return try apply(UnsafeMutableRawBufferPointer(start: d.mutableBytes.advanced(by:range.lowerBound - _offset), count: Swift.min(range.count, len - range.lowerBound)))
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
_backing = .mutable(data)
_bytes = data.mutableBytes
return try apply(UnsafeMutableRawBufferPointer(start: _bytes!.advanced(by:range.lowerBound - _offset), count: Swift.min(range.count, _length - range.lowerBound)))
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
_backing = .customMutableReference(data)
let len = data.length
return try apply(UnsafeMutableRawBufferPointer(start: data.mutableBytes.advanced(by:range.lowerBound - _offset), count: Swift.min(range.count, len - range.lowerBound)))
}
}
public var mutableBytes: UnsafeMutableRawPointer? {
@inline(__always)
get {
switch _backing {
case .swift:
return _bytes
return _bytes?.advanced(by: -_offset)
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.length = length
_backing = .mutable(data)
_bytes = data.mutableBytes
return data.mutableBytes
return _bytes?.advanced(by: -_offset)
case .mutable:
return _bytes
return _bytes?.advanced(by: -_offset)
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.length = length
_backing = .customMutableReference(data)
_bytes = data.mutableBytes
return data.mutableBytes
return data.mutableBytes.advanced(by: -_offset)
case .customMutableReference(let d):
return d.mutableBytes
return d.mutableBytes.advanced(by: -_offset)
}
}
}
@@ -176,32 +273,34 @@ public final class _DataStorage {
}
}
public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer<UInt8>, _ byteIndex: Data.Index, _ stop: inout Bool) -> Void) {
var stop: Bool = false
public func enumerateBytes(in range: Range<Int>, _ block: (_ buffer: UnsafeBufferPointer<UInt8>, _ byteIndex: Data.Index, _ stop: inout Bool) -> Void) {
var stopv: Bool = false
var data: NSData
switch _backing {
case .swift:
block(UnsafeBufferPointer<UInt8>(start: _bytes?.assumingMemoryBound(to: UInt8.self), count: _length), 0, &stop)
case .immutable:
block(UnsafeBufferPointer<UInt8>(start: _bytes?.assumingMemoryBound(to: UInt8.self), count: _length), 0, &stop)
case .mutable:
block(UnsafeBufferPointer<UInt8>(start: _bytes?.assumingMemoryBound(to: UInt8.self), count: _length), 0, &stop)
case .swift: fallthrough
case .immutable: fallthrough
case .mutable:
block(UnsafeBufferPointer<UInt8>(start: _bytes?.advanced(by: range.lowerBound - _offset).assumingMemoryBound(to: UInt8.self), count: Swift.min(range.count, _length)), 0, &stopv)
return
case .customReference(let d):
d.enumerateBytes { (ptr, range, stop) in
var stopv = false
let bytePtr = ptr.bindMemory(to: UInt8.self, capacity: range.length)
block(UnsafeBufferPointer(start: bytePtr, count: range.length), range.location, &stopv)
if stopv {
stop.pointee = true
}
}
data = d
break
case .customMutableReference(let d):
d.enumerateBytes { (ptr, range, stop) in
var stopv = false
let bytePtr = ptr.bindMemory(to: UInt8.self, capacity: range.length)
block(UnsafeBufferPointer(start: bytePtr, count: range.length), range.location, &stopv)
if stopv {
stop.pointee = true
}
data = d
break
}
data.enumerateBytes { (ptr, region, stop) in
// any region that is not in the range should be skipped
guard range.contains(region.lowerBound) || range.contains(region.upperBound) else { return }
var regionAdjustment = 0
if region.lowerBound < range.lowerBound {
regionAdjustment = range.lowerBound - (region.lowerBound - _offset)
}
let bytePtr = ptr.advanced(by: regionAdjustment).assumingMemoryBound(to: UInt8.self)
let effectiveLength = Swift.min((region.location - _offset) + region.length, range.upperBound) - (region.location - _offset)
block(UnsafeBufferPointer(start: bytePtr, count: effectiveLength - regionAdjustment), region.location + regionAdjustment - _offset, &stopv)
if stopv {
stop.pointee = true
}
}
}
@@ -319,7 +418,7 @@ public final class _DataStorage {
@inline(__always)
public func append(_ bytes: UnsafeRawPointer, length: Int) {
precondition(length >= 0, "Length of appending bytes must be positive")
precondition(length >= 0, "Length of appending bytes must not be negative")
switch _backing {
case .swift:
let origLength = _length
@@ -398,6 +497,43 @@ public final class _DataStorage {
}
}
public func get(_ index: Int) -> UInt8 {
switch _backing {
case .swift: fallthrough
case .immutable: fallthrough
case .mutable:
return _bytes!.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee
case .customReference(let d):
if d._isCompact() {
return d.bytes.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee
} else {
var byte: UInt8 = 0
d.enumerateBytes { (ptr, range, stop) in
if NSLocationInRange(index, range) {
let offset = index - range.location - _offset
byte = ptr.advanced(by: offset).assumingMemoryBound(to: UInt8.self).pointee
stop.pointee = true
}
}
return byte
}
case .customMutableReference(let d):
if d._isCompact() {
return d.bytes.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee
} else {
var byte: UInt8 = 0
d.enumerateBytes { (ptr, range, stop) in
if NSLocationInRange(index, range) {
let offset = index - range.location - _offset
byte = ptr.advanced(by: offset).assumingMemoryBound(to: UInt8.self).pointee
stop.pointee = true
}
}
return byte
}
}
}
@inline(__always)
public func set(_ index: Int, to value: UInt8) {
@@ -405,7 +541,7 @@ public final class _DataStorage {
case .swift:
fallthrough
case .mutable:
_bytes!.advanced(by: index).assumingMemoryBound(to: UInt8.self).pointee = value
_bytes!.advanced(by: index - _offset).assumingMemoryBound(to: UInt8.self).pointee = value
default:
var theByte = value
let range = NSRange(location: index, length: 1)
@@ -427,28 +563,29 @@ public final class _DataStorage {
}
_length = newLength
}
_DataStorage.move(_bytes!.advanced(by: range.location), bytes!, range.length)
_DataStorage.move(_bytes!.advanced(by: range.location - _offset), bytes!, range.length)
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.replaceBytes(in: range, withBytes: bytes!)
data.replaceBytes(in: NSRange(location: range.location - _offset, length: range.length), withBytes: bytes!)
_backing = .mutable(data)
_length = data.length
_bytes = data.mutableBytes
case .mutable(let d):
d.replaceBytes(in: range, withBytes: bytes!)
d.replaceBytes(in: NSRange(location: range.location - _offset, length: range.length), withBytes: bytes!)
_length = d.length
_bytes = d.mutableBytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.replaceBytes(in: range, withBytes: bytes!)
data.replaceBytes(in: NSRange(location: range.location - _offset, length: range.length), withBytes: bytes!)
_backing = .customMutableReference(data)
case .customMutableReference(let d):
d.replaceBytes(in: range, withBytes: bytes!)
d.replaceBytes(in: NSRange(location: range.location - _offset, length: range.length), withBytes: bytes!)
}
}
@inline(__always)
public func replaceBytes(in range: NSRange, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) {
public func replaceBytes(in range_: NSRange, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) {
let range = NSRange(location: range_.location - _offset, length: range_.length)
let currentLength = _length
let resultingLength = currentLength - range.length + replacementLength
switch _backing {
@@ -497,7 +634,8 @@ public final class _DataStorage {
}
@inline(__always)
public func resetBytes(in range: NSRange) {
public func resetBytes(in range_: NSRange) {
let range = NSRange(location: range_.location - _offset, length: range_.length)
if range.length == 0 { return }
switch _backing {
case .swift:
@@ -546,6 +684,7 @@ public final class _DataStorage {
_capacity = capacity
_needToZero = !clear
_length = 0
_offset = 0
setLength(length)
}
@@ -560,10 +699,12 @@ public final class _DataStorage {
_bytes = _DataStorage.allocate(capacity, false)!
_capacity = capacity
_needToZero = true
_offset = 0
}
public init(bytes: UnsafeRawPointer?, length: Int) {
precondition(length < _DataStorage.maxSize)
_offset = 0
if length == 0 {
_capacity = 0
_length = 0
@@ -588,8 +729,9 @@ public final class _DataStorage {
}
}
public init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) {
public init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?, offset: Int) {
precondition(length < _DataStorage.maxSize)
_offset = offset
if length == 0 {
_capacity = 0
_length = 0
@@ -630,7 +772,8 @@ public final class _DataStorage {
}
}
public init(immutableReference: NSData) {
public init(immutableReference: NSData, offset: Int) {
_offset = offset
_bytes = UnsafeMutableRawPointer(mutating: immutableReference.bytes)
_capacity = 0
_needToZero = false
@@ -638,7 +781,8 @@ public final class _DataStorage {
_backing = .immutable(immutableReference)
}
public init(mutableReference: NSMutableData) {
public init(mutableReference: NSMutableData, offset: Int) {
_offset = offset
_bytes = mutableReference.mutableBytes
_capacity = 0
_needToZero = false
@@ -646,7 +790,8 @@ public final class _DataStorage {
_backing = .mutable(mutableReference)
}
public init(customReference: NSData) {
public init(customReference: NSData, offset: Int) {
_offset = offset
_bytes = nil
_capacity = 0
_needToZero = false
@@ -654,7 +799,8 @@ public final class _DataStorage {
_backing = .customReference(customReference)
}
public init(customMutableReference: NSMutableData) {
public init(customMutableReference: NSMutableData, offset: Int) {
_offset = offset
_bytes = nil
_capacity = 0
_needToZero = false
@@ -675,30 +821,30 @@ public final class _DataStorage {
public func mutableCopy(_ range: Range<Int>) -> _DataStorage {
switch _backing {
case .swift:
return _DataStorage(bytes: _bytes?.advanced(by: range.lowerBound), length: range.count, copy: true, deallocator: nil)
return _DataStorage(bytes: _bytes?.advanced(by: range.lowerBound - _offset), length: range.count, copy: true, deallocator: nil, offset: range.lowerBound)
case .immutable(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData, offset: range.lowerBound)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData, offset: range.lowerBound)
}
case .mutable(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData, offset: range.lowerBound)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData, offset: range.lowerBound)
}
case .customReference(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData, offset: range.lowerBound)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData, offset: range.lowerBound)
}
case .customMutableReference(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData, offset: range.lowerBound)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData, offset: range.lowerBound)
}
}
}
@@ -710,26 +856,26 @@ public final class _DataStorage {
switch _backing {
case .swift:
return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false))
return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound - _offset), length: range.count, freeWhenDone: false))
case .immutable(let d):
guard range.lowerBound == 0 && range.upperBound == _length else {
return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false))
return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound - _offset), length: range.count, freeWhenDone: false))
}
return try work(d)
case .mutable(let d):
guard range.lowerBound == 0 && range.upperBound == _length else {
return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false))
return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound - _offset), length: range.count, freeWhenDone: false))
}
return try work(d)
case .customReference(let d):
guard range.lowerBound == 0 && range.upperBound == _length else {
return try work(NSData(bytesNoCopy: UnsafeMutableRawPointer(mutating: d.bytes.advanced(by: range.lowerBound)), length: range.count, freeWhenDone: false))
return try work(NSData(bytesNoCopy: UnsafeMutableRawPointer(mutating: d.bytes.advanced(by: range.lowerBound - _offset)), length: range.count, freeWhenDone: false))
}
return try work(d)
case .customMutableReference(let d):
guard range.lowerBound == 0 && range.upperBound == _length else {
return try work(NSData(bytesNoCopy: UnsafeMutableRawPointer(mutating: d.bytes.advanced(by: range.lowerBound)), length: range.count, freeWhenDone: false))
return try work(NSData(bytesNoCopy: UnsafeMutableRawPointer(mutating: d.bytes.advanced(by: range.lowerBound - _offset)), length: range.count, freeWhenDone: false))
}
return try work(d)
}
@@ -766,26 +912,14 @@ public final class _DataStorage {
}
}
public var hashValue: Int {
switch _backing {
case .customReference(let d):
return d.hash
case .customMutableReference(let d):
return d.hash
default:
let len = _length
return Int(bitPattern: CFHashBytes(_bytes?.assumingMemoryBound(to: UInt8.self), Swift.min(len, 80)))
}
}
public func subdata(in range: Range<Data.Index>) -> Data {
switch _backing {
case .customReference(let d):
return d.subdata(with: NSRange(location: range.lowerBound, length: range.count))
return d.subdata(with: NSRange(location: range.lowerBound - _offset, length: range.count))
case .customMutableReference(let d):
return d.subdata(with: NSRange(location: range.lowerBound, length: range.count))
return d.subdata(with: NSRange(location: range.lowerBound - _offset, length: range.count))
default:
return Data(bytes: _bytes!.advanced(by: range.lowerBound), count: range.count)
return Data(bytes: _bytes!.advanced(by: range.lowerBound - _offset), count: range.count)
}
}
}
@@ -827,7 +961,7 @@ internal class _NSSwiftData : NSData {
}
#if !DEPLOYMENT_RUNTIME_SWIFT
@objc
@objc override
func _isCompact() -> Bool {
return true
}
@@ -1015,7 +1149,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - parameter deallocator: Specifies the mechanism to free the indicated buffer, or `.none`.
public init(bytesNoCopy bytes: UnsafeMutableRawPointer, count: Int, deallocator: Deallocator) {
let whichDeallocator = deallocator._deallocator
_backing = _DataStorage(bytes: bytes, length: count, copy: false, deallocator: whichDeallocator)
_backing = _DataStorage(bytes: bytes, length: count, copy: false, deallocator: whichDeallocator, offset: 0)
_sliceRange = 0..<count
}
@@ -1026,7 +1160,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - throws: An error in the Cocoa domain, if `url` cannot be read.
public init(contentsOf url: URL, options: Data.ReadingOptions = []) throws {
let d = try NSData(contentsOf: url, options: ReadingOptions(rawValue: options.rawValue))
_backing = _DataStorage(immutableReference: d)
_backing = _DataStorage(immutableReference: d, offset: 0)
_sliceRange = 0..<d.length
}
@@ -1037,7 +1171,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - 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)) {
_backing = _DataStorage(immutableReference: d)
_backing = _DataStorage(immutableReference: d, offset: 0)
_sliceRange = 0..<d.length
} else {
return nil
@@ -1052,7 +1186,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - parameter options: Decoding options. Default value is `[]`.
public init?(base64Encoded base64Data: Data, options: Data.Base64DecodingOptions = []) {
if let d = NSData(base64Encoded: base64Data, options: Base64DecodingOptions(rawValue: options.rawValue)) {
_backing = _DataStorage(immutableReference: d)
_backing = _DataStorage(immutableReference: d, offset: 0)
_sliceRange = 0..<d.length
} else {
return nil
@@ -1073,10 +1207,10 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
let providesConcreteBacking = (reference as AnyObject)._providesConcreteBacking?() ?? false
#endif
if providesConcreteBacking {
_backing = _DataStorage(immutableReference: reference.copy() as! NSData)
_backing = _DataStorage(immutableReference: reference.copy() as! NSData, offset: 0)
_sliceRange = 0..<reference.length
} else {
_backing = _DataStorage(customReference: reference.copy() as! NSData)
_backing = _DataStorage(customReference: reference.copy() as! NSData, offset: 0)
_sliceRange = 0..<reference.length
}
@@ -1094,7 +1228,11 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
} else if let buffer = elements as? UnsafeMutableBufferPointer<UInt8> {
self.init(buffer: buffer)
} else if let data = elements as? Data {
self.init(backing: data._backing.mutableCopy(data._sliceRange), range: 0..<data.count)
let len = data.count
let backing = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) in
return _DataStorage(bytes: bytes, length: len)
}
self.init(backing: backing, range: 0..<len)
} else {
let underestimatedCount = elements.underestimatedCount
self.init(count: underestimatedCount)
@@ -1137,7 +1275,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
}
@inline(__always)
set {
precondition(count >= 0, "Count must be positive")
precondition(count >= 0, "count must not be negative")
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
@@ -1151,9 +1289,9 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
@inline(__always)
public func withUnsafeBytes<ResultType, ContentType>(_ body: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
let bytes = _backing.bytes?.advanced(by: _sliceRange.lowerBound) ?? UnsafeRawPointer(bitPattern: 0xBAD0)!
let contentPtr = bytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout<ContentType>.stride)
return try body(contentPtr)
return try _backing.withUnsafeBytes(in: _sliceRange) {
return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafePointer<ContentType>(bitPattern: 0xBAD0)!)
}
}
@@ -1166,9 +1304,9 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
let mutableBytes = _backing.mutableBytes?.advanced(by: _sliceRange.lowerBound) ?? UnsafeMutableRawPointer(bitPattern: 0xBAD0)!
let contentPtr = mutableBytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout<ContentType>.stride)
return try body(UnsafeMutablePointer(contentPtr))
return try _backing.withUnsafeMutableBytes(in: _sliceRange) {
return try body($0.baseAddress?.assumingMemoryBound(to: ContentType.self) ?? UnsafeMutablePointer<ContentType>(bitPattern: 0xBAD0)!)
}
}
// MARK: -
@@ -1181,15 +1319,19 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - warning: This method does not verify that the contents at pointer have enough space to hold `count` bytes.
@inline(__always)
public func copyBytes(to pointer: UnsafeMutablePointer<UInt8>, count: Int) {
precondition(count >= 0, "Count of bytes to copy must be positive")
precondition(count >= 0, "count of bytes to copy must not be negative")
if count == 0 { return }
memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: _sliceRange.lowerBound), Swift.min(count, _sliceRange.count))
_backing.withUnsafeBytes(in: _sliceRange) {
memcpy(UnsafeMutableRawPointer(pointer), $0.baseAddress!, Swift.min(count, $0.count))
}
}
@inline(__always)
private func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: NSRange) {
if range.length == 0 { return }
memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: range.location), Swift.min(range.length, _sliceRange.count))
_backing.withUnsafeBytes(in: range.lowerBound..<range.upperBound) {
memcpy(UnsafeMutableRawPointer(pointer), $0.baseAddress!, Swift.min(range.length, $0.count))
}
}
/// Copy a subset of the contents of the data to a pointer.
@@ -1297,27 +1439,20 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// In some cases, (for example, a `Data` backed by a `dispatch_data_t`, the bytes may be stored discontiguously. In those cases, this function invokes the closure for each contiguous region of bytes.
/// - parameter block: The closure to invoke for each region of data. You may stop the enumeration by setting the `stop` parameter to `true`.
public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer<UInt8>, _ byteIndex: Index, _ stop: inout Bool) -> Void) {
_backing.enumerateBytes(block)
_backing.enumerateBytes(in: _sliceRange, block)
}
@inline(__always)
public mutating func append(_ bytes: UnsafePointer<UInt8>, count: Int) {
precondition(count >= 0, "Count must be positive")
if count == 0 { return }
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.append(bytes, length: count)
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.upperBound + count)
append(UnsafeBufferPointer(start: bytes, count: count))
}
@inline(__always)
public mutating func append(_ other: Data) {
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
other.enumerateBytes { (buffer, _, _) in
append(buffer)
}
_backing.append(other._backing, startingAt: other._sliceRange.lowerBound, endingAt: other._sliceRange.upperBound)
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.upperBound + other.count)
}
/// Append a buffer of bytes to the data.
@@ -1329,22 +1464,26 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.append(buffer.baseAddress!, length: buffer.count * MemoryLayout<SourceType>.stride)
_backing.replaceBytes(in: NSRange(location: _sliceRange.upperBound, length: _backing.length - (_sliceRange.upperBound - _backing._offset)), with: buffer.baseAddress, length: buffer.count * MemoryLayout<SourceType>.stride)
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.upperBound + buffer.count * MemoryLayout<SourceType>.stride)
}
@inline(__always)
public mutating func append<S : Sequence>(contentsOf newElements: S) where S.Iterator.Element == Iterator.Element {
let estimatedCount = newElements.underestimatedCount
var idx = count
count += estimatedCount
for byte in newElements {
let newIndex = idx + 1
if newIndex > count {
count = newIndex
guard estimatedCount > 0 else {
for byte in newElements {
append(byte)
}
return
}
_withStackOrHeapBuffer(estimatedCount) { allocation in
let buffer = UnsafeMutableBufferPointer(start: allocation.pointee.memory.assumingMemoryBound(to: UInt8.self), count: estimatedCount)
var (iterator, endPoint) = newElements._copyContents(initializing: buffer)
append(buffer.baseAddress!, count: endPoint - buffer.startIndex)
while let byte = iterator.next() {
append(byte)
}
self[idx] = byte
idx = newIndex
}
}
@@ -1364,16 +1503,15 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
@inline(__always)
public mutating func resetBytes(in range: Range<Index>) {
// it is worth noting that the range here may be out of bounds of the Data itself (which triggers a growth)
precondition(range.lowerBound >= 0, "Ranges must be positive bounds")
precondition(range.upperBound >= 0, "Ranges must be positive bounds")
precondition(range.lowerBound >= 0, "Ranges must not be negative bounds")
precondition(range.upperBound >= 0, "Ranges must not be negative bounds")
let range = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound)
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.resetBytes(in: range)
if _sliceRange.count < range.location + range.length {
let newLength = range.location + range.length
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + newLength)
if _sliceRange.upperBound < range.upperBound {
_sliceRange = _sliceRange.lowerBound..<range.upperBound
}
}
@@ -1386,34 +1524,16 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - parameter data: The replacement data.
@inline(__always)
public mutating func replaceSubrange(_ subrange: Range<Index>, with data: Data) {
_validateRange(subrange)
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
let cnt = data.count
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
data.withUnsafeBytes {
replaceSubrange(subrange, with: $0, count: cnt)
}
let resultingLength = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Int in
let currentLength = _backing.length
_backing.replaceBytes(in: nsRange, with: bytes, length: cnt)
return currentLength - nsRange.length + cnt
}
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
}
@inline(__always)
public mutating func replaceSubrange(_ subrange: CountableRange<Index>, with data: Data) {
_validateRange(subrange)
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
let cnt = data.count
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
let resultingLength = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Int in
let currentLength = _backing.length
_backing.replaceBytes(in: nsRange, with: bytes, length: cnt)
return currentLength - nsRange.length + cnt
}
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
let range: Range<Int> = subrange.lowerBound..<subrange.upperBound
replaceSubrange(range, with: data)
}
/// Replace a region of bytes in the data with new bytes from a buffer.
@@ -1425,17 +1545,8 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - parameter buffer: The replacement bytes.
@inline(__always)
public mutating func replaceSubrange<SourceType>(_ subrange: Range<Index>, with buffer: UnsafeBufferPointer<SourceType>) {
_validateRange(subrange)
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
let bufferCount = buffer.count * MemoryLayout<SourceType>.stride
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
let currentLength = _backing.length
_backing.replaceBytes(in: nsRange, with: buffer.baseAddress, length: bufferCount)
let resultingLength = currentLength - nsRange.length + bufferCount
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
guard buffer.count > 0 else { return }
replaceSubrange(subrange, with: buffer.baseAddress!, count: buffer.count * MemoryLayout<SourceType>.stride)
}
/// Replace a region of bytes in the data with new bytes from a collection.
@@ -1446,39 +1557,18 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// - parameter subrange: The range in the data to replace.
/// - parameter newElements: The replacement bytes.
@inline(__always)
public mutating func replaceSubrange<ByteCollection : Collection>(_ subrange: Range<Index>, with newElements: ByteCollection)
where ByteCollection.Iterator.Element == Data.Iterator.Element {
_validateRange(subrange)
// Calculate this once, it may not be O(1)
let replacementCount: Int = numericCast(newElements.count)
let currentCount = self.count
let subrangeCount = subrange.count
_validateRange(subrange)
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<UInt8>) -> Void in
if shift != 0 {
let destination = bytes + start + replacementCount
let source = bytes + start + subrangeCount
memmove(destination, source, currentCount - start - subrangeCount)
}
if replacementCount != 0 {
let buf = UnsafeMutableBufferPointer(start: bytes + start, count: replacementCount)
var (it,idx) = newElements._copyContents(initializing: buf)
precondition(it.next() == nil && idx == buf.endIndex, "newElements iterator returned different count to newElements.count")
}
public mutating func replaceSubrange<ByteCollection : Collection>(_ subrange: Range<Index>, with newElements: ByteCollection) where ByteCollection.Iterator.Element == Data.Iterator.Element {
_validateRange(subrange)
let totalCount: Int = numericCast(newElements.count)
_withStackOrHeapBuffer(totalCount) { conditionalBuffer in
let buffer = UnsafeMutableBufferPointer(start: conditionalBuffer.pointee.memory.assumingMemoryBound(to: UInt8.self), count: totalCount)
var (iterator, index) = newElements._copyContents(initializing: buffer)
while let byte = iterator.next() {
buffer[index] = byte
index = buffer.index(after: index)
}
replaceSubrange(subrange, with: conditionalBuffer.pointee.memory, count: totalCount)
}
}
@inline(__always)
@@ -1488,10 +1578,10 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
let currentLength = _backing.length
let upper = _sliceRange.upperBound
_backing.replaceBytes(in: nsRange, with: bytes, length: cnt)
let resultingLength = currentLength - nsRange.length + cnt
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
let resultingUpper = upper - nsRange.length + cnt
_sliceRange = _sliceRange.lowerBound..<resultingUpper
}
/// Return a new copy of the data in a specified range.
@@ -1534,7 +1624,15 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
/// The hash value for the data.
public var hashValue: Int {
return _backing.hashValue
var hashValue = 0
let hashRange: Range<Int> = _sliceRange.lowerBound..<Swift.min(_sliceRange.lowerBound + 80, _sliceRange.upperBound)
_withStackOrHeapBuffer(hashRange.count) { buffer in
_backing.withUnsafeBytes(in: hashRange) {
memcpy(buffer.pointee.memory, $0.baseAddress!, hashRange.count)
}
hashValue = Int(bitPattern: CFHashBytes(buffer.pointee.memory.assumingMemoryBound(to: UInt8.self), hashRange.count))
}
return hashValue
}
@inline(__always)
@@ -1557,7 +1655,7 @@ public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessColl
@inline(__always)
get {
_validateIndex(index)
return _backing.bytes!.advanced(by: index).assumingMemoryBound(to: UInt8.self).pointee
return _backing.get(index)
}
@inline(__always)
set {
@@ -1770,7 +1868,7 @@ extension Data : CustomStringConvertible, CustomDebugStringConvertible, CustomRe
// Minimal size data is output as an array
if nBytes < 64 {
children.append((label: "bytes", value: Array(self[0..<nBytes])))
children.append((label: "bytes", value: Array(self[startIndex..<Swift.min(nBytes + startIndex, endIndex)])))
}
let m = Mirror(self, children:children, displayStyle: Mirror.DisplayStyle.struct)
@@ -1868,4 +1966,3 @@ extension Data : Codable {
}
}
}