Files
swift-mirror/stdlib/public/SDK/Foundation/Data.swift
Philippe Hausler dc783c064c [Foundation] Remove @_silgen thunks and replace them with shims instead
This avoids indirection by making calls directly to the C implementations which prevents potentials of mismatched intent or changes of calling convention of @_silgen. The added benefit is that all of the shims in this case are no longer visible symbols (anyone using them was not authorized out side of the Foundation overlay). Also the callout methods in the headers now all share similar naming shcemes for easier refactoring and searching in the style of __NS<class><action> style. The previous compiled C/Objective-C source files were built with MRR the new headers MUST be ARC by Swift import rules.

The one caveat is that certain functions MUST avoid the bridge case (since they are part of the bridging code-paths and that would incur a recursive potential) which have the types erased up to NSObject * via the macro NS_NON_BRIDGED.

The remaining @_silgen declarations are either swift functions exposed externally to the rest of Swift’s runtime or are included in NSNumber.gyb which the Foundation team has other plans for removing those @_silgen functions at a later date and Data.swift has one external function left with @_silgen which is blocked by a bug in the compiler which seems to improperly import that particular method as an inline c function.
2017-03-06 09:59:37 -08:00

1710 lines
66 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if DEPLOYMENT_RUNTIME_SWIFT
#if os(OSX) || os(iOS)
import Darwin
#elseif os(Linux)
import Glibc
#endif
import CoreFoundation
internal func __NSDataInvokeDeallocatorUnmap(_ mem: UnsafeMutableRawPointer, _ length: Int) {
munmap(mem, length)
}
internal func __NSDataInvokeDeallocatorFree(_ mem: UnsafeMutableRawPointer, _ length: Int) {
free(mem)
}
#else
@_exported import Foundation // Clang module
import _SwiftFoundationOverlayShims
import _SwiftCoreFoundationOverlayShims
@_silgen_name("__NSDataWriteToURL")
internal func __NSDataWriteToURL(_ data: NSData, _ url: NSURL, _ options: UInt, _ error: NSErrorPointer) -> Bool
#endif
public final class _DataStorage {
public enum Backing {
// A mirror of the Objective-C implementation that is suitable to inline in Swift
case swift
// these two storage points for immutable and mutable data are reserved for references that are returned by "known"
// cases from Foundation in which implement the backing of struct Data, these have signed up for the concept that
// the backing bytes/mutableBytes pointer does not change per call (unless mutated) as well as the length is ok
// to be cached, this means that as long as the backing reference is retained no further objc_msgSends need to be
// dynamically dispatched out to the reference.
case immutable(NSData) // This will most often (perhaps always) be NSConcreteData
case mutable(NSMutableData) // This will often (perhaps always) be NSConcreteMutableData
// These are reserved for foreign sources where neither Swift nor Foundation are fully certain whom they belong
// to from an object inheritance standpoint, this means that all bets are off and the values of bytes, mutableBytes,
// and length cannot be cached. This also means that all methods are expected to dynamically dispatch out to the
// backing reference.
case customReference(NSData) // tracks data references that are only known to be immutable
case customMutableReference(NSMutableData) // tracks data references that are known to be mutable
}
public static let maxSize = Int.max >> 1
public static let vmOpsThreshold = NSPageSize() * 4
public static func allocate(_ size: Int, _ clear: Bool) -> UnsafeMutableRawPointer? {
if clear {
return calloc(1, size)
} else {
return malloc(size)
}
}
public static func move(_ dest_: UnsafeMutableRawPointer, _ source_: UnsafeRawPointer?, _ num_: Int) {
var dest = dest_
var source = source_
var num = num_
if _DataStorage.vmOpsThreshold <= num && ((unsafeBitCast(source, to: Int.self) | Int(bitPattern: dest)) & (NSPageSize() - 1)) == 0 {
let pages = NSRoundDownToMultipleOfPageSize(num)
NSCopyMemoryPages(source!, dest, pages)
source = source!.advanced(by: pages)
dest = dest.advanced(by: pages)
num -= pages
}
if num > 0 {
memmove(dest, source!, num)
}
}
public static func shouldAllocateCleared(_ size: Int) -> Bool {
return (size > (128 * 1024))
}
public var _bytes: UnsafeMutableRawPointer?
public var _length: Int
public var _capacity: Int
public var _needToZero: Bool
public var _deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?
public var _backing: Backing = .swift
public var bytes: UnsafeRawPointer? {
@inline(__always)
get {
switch _backing {
case .swift:
return UnsafeRawPointer(_bytes)
case .immutable:
return UnsafeRawPointer(_bytes)
case .mutable:
return UnsafeRawPointer(_bytes)
case .customReference(let d):
return d.bytes
case .customMutableReference(let d):
return d.bytes
}
}
}
public var mutableBytes: UnsafeMutableRawPointer? {
@inline(__always)
get {
switch _backing {
case .swift:
return _bytes
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.length = length
_backing = .mutable(data)
_bytes = data.mutableBytes
return data.mutableBytes
case .mutable:
return _bytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.length = length
_backing = .customMutableReference(data)
_bytes = data.mutableBytes
return data.mutableBytes
case .customMutableReference(let d):
return d.mutableBytes
}
}
}
public var length: Int {
@inline(__always)
get {
switch _backing {
case .swift:
return _length
case .immutable:
return _length
case .mutable:
return _length
case .customReference(let d):
return d.length
case .customMutableReference(let d):
return d.length
}
}
@inline(__always)
set {
setLength(newValue)
}
}
public func _freeBytes() {
if let bytes = _bytes {
if let dealloc = _deallocator {
dealloc(bytes, length)
} else {
free(bytes)
}
}
}
public func enumerateBytes(_ block: (_ buffer: UnsafeBufferPointer<UInt8>, _ byteIndex: Data.Index, _ stop: inout Bool) -> Void) {
var stop: Bool = false
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 .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.length, &stopv)
if stopv {
stop.pointee = true
}
}
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.length, &stopv)
if stopv {
stop.pointee = true
}
}
}
}
@inline(never)
public func _grow(_ newLength: Int, _ clear: Bool) {
let cap = _capacity
var additionalCapacity = (newLength >> (_DataStorage.vmOpsThreshold <= newLength ? 2 : 1))
if Int.max - additionalCapacity < newLength {
additionalCapacity = 0
}
var newCapacity = Swift.max(cap, newLength + additionalCapacity)
let origLength = _length
var allocateCleared = clear && _DataStorage.shouldAllocateCleared(newCapacity)
var newBytes: UnsafeMutableRawPointer? = nil
if _bytes == nil {
newBytes = _DataStorage.allocate(newCapacity, allocateCleared)
if newBytes == nil {
/* Try again with minimum length */
allocateCleared = clear && _DataStorage.shouldAllocateCleared(newLength)
newBytes = _DataStorage.allocate(newLength, allocateCleared)
}
} else {
let tryCalloc = (origLength == 0 || (newLength / origLength) >= 4)
if allocateCleared && tryCalloc {
newBytes = _DataStorage.allocate(newCapacity, true)
if newBytes != nil {
_DataStorage.move(newBytes!, _bytes!, origLength)
_freeBytes()
}
}
/* Where calloc/memmove/free fails, realloc might succeed */
if newBytes == nil {
allocateCleared = false
if _deallocator != nil {
newBytes = _DataStorage.allocate(newCapacity, true)
if newBytes != nil {
_DataStorage.move(newBytes!, _bytes!, origLength)
_freeBytes()
_deallocator = nil
}
} else {
newBytes = realloc(_bytes!, newCapacity)
}
}
/* Try again with minimum length */
if newBytes == nil {
newCapacity = newLength
allocateCleared = clear && _DataStorage.shouldAllocateCleared(newCapacity)
if allocateCleared && tryCalloc {
newBytes = _DataStorage.allocate(newCapacity, true)
if newBytes != nil {
_DataStorage.move(newBytes!, _bytes!, origLength)
_freeBytes()
}
}
if newBytes == nil {
allocateCleared = false
newBytes = realloc(_bytes!, newCapacity)
}
}
}
if newBytes == nil {
/* Could not allocate bytes */
// At this point if the allocation cannot occur the process is likely out of memory
// and Bad-Things are going to happen anyhow
fatalError("unable to allocate memory for length (\(newLength))")
}
if origLength < newLength && clear && !allocateCleared {
memset(newBytes!.advanced(by: origLength), 0, newLength - origLength)
}
/* _length set by caller */
_bytes = newBytes
_capacity = newCapacity
/* Realloc/memset doesn't zero out the entire capacity, so we must be safe and clear next time we grow the length */
_needToZero = !allocateCleared
}
@inline(__always)
public func setLength(_ length: Int) {
switch _backing {
case .swift:
let origLength = _length
let newLength = length
if _capacity < newLength || _bytes == nil {
_grow(newLength, true)
} else if origLength < newLength && _needToZero {
memset(_bytes! + origLength, 0, newLength - origLength)
} else if newLength < origLength {
_needToZero = true
}
_length = newLength
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.length = length
_backing = .mutable(data)
_length = length
_bytes = data.mutableBytes
case .mutable(let d):
d.length = length
_length = length
_bytes = d.mutableBytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.length = length
_backing = .customMutableReference(data)
case .customMutableReference(let d):
d.length = length
}
}
@inline(__always)
public func append(_ bytes: UnsafeRawPointer, length: Int) {
switch _backing {
case .swift:
let origLength = _length
let newLength = origLength + length
if _capacity < newLength || _bytes == nil {
_grow(newLength, false)
}
_length = newLength
_DataStorage.move(_bytes!.advanced(by: origLength), bytes, length)
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.append(bytes, length: length)
_backing = .mutable(data)
_length = data.length
_bytes = data.mutableBytes
case .mutable(let d):
d.append(bytes, length: length)
_length = d.length
_bytes = d.mutableBytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.append(bytes, length: length)
_backing = .customReference(data)
case .customMutableReference(let d):
d.append(bytes, length: length)
}
}
// fast-path for appending directly from another data storage
@inline(__always)
public func append(_ otherData: _DataStorage) {
let otherLength = otherData.length
if otherLength == 0 { return }
if let bytes = otherData.bytes {
append(bytes, length: otherLength)
}
}
@inline(__always)
public func append(_ otherData: Data) {
otherData.enumerateBytes { (buffer: UnsafeBufferPointer<UInt8>, _, _) in
append(buffer.baseAddress!, length: buffer.count)
}
}
@inline(__always)
public func increaseLength(by extraLength: Int) {
if extraLength == 0 { return }
switch _backing {
case .swift:
let origLength = _length
let newLength = origLength + extraLength
if _capacity < newLength || _bytes == nil {
_grow(newLength, true)
} else if _needToZero {
memset(_bytes!.advanced(by: origLength), 0, extraLength)
}
_length = newLength
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.increaseLength(by: extraLength)
_backing = .mutable(data)
_length += extraLength
_bytes = data.mutableBytes
case .mutable(let d):
d.increaseLength(by: extraLength)
_length += extraLength
_bytes = d.mutableBytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.increaseLength(by: extraLength)
_backing = .customReference(data)
case .customMutableReference(let d):
d.increaseLength(by: extraLength)
}
}
@inline(__always)
public func set(_ index: Int, to value: UInt8) {
switch _backing {
case .swift:
fallthrough
case .mutable:
_bytes!.advanced(by: index).assumingMemoryBound(to: UInt8.self).pointee = value
default:
var theByte = value
let range = NSRange(location: index, length: 1)
replaceBytes(in: range, with: &theByte, length: 1)
}
}
@inline(__always)
public func replaceBytes(in range: NSRange, with bytes: UnsafeRawPointer?) {
if range.length == 0 { return }
switch _backing {
case .swift:
if _length < range.location + range.length {
let newLength = range.location + range.length
if _capacity < newLength {
_grow(newLength, false)
}
_length = newLength
}
_DataStorage.move(_bytes!.advanced(by: range.location), bytes!, range.length)
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.replaceBytes(in: range, withBytes: bytes!)
_backing = .mutable(data)
_length = data.length
_bytes = data.mutableBytes
case .mutable(let d):
d.replaceBytes(in: range, withBytes: bytes!)
_length = d.length
_bytes = d.mutableBytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.replaceBytes(in: range, withBytes: bytes!)
_backing = .customMutableReference(data)
case .customMutableReference(let d):
d.replaceBytes(in: range, withBytes: bytes!)
}
}
@inline(__always)
public func replaceBytes(in range: NSRange, with replacementBytes: UnsafeRawPointer?, length replacementLength: Int) {
let currentLength = _length
let resultingLength = currentLength - range.length + replacementLength
switch _backing {
case .swift:
let shift = resultingLength - currentLength
var mutableBytes = _bytes
if resultingLength > currentLength {
setLength(resultingLength)
mutableBytes = _bytes!
}
/* shift the trailing bytes */
let start = range.location
let length = range.length
if shift != 0 {
memmove(mutableBytes! + start + replacementLength, mutableBytes! + start + length, currentLength - start - length)
}
if replacementLength != 0 {
if replacementBytes != nil {
memmove(mutableBytes! + start, replacementBytes!, replacementLength)
} else {
memset(mutableBytes! + start, 0, replacementLength)
}
}
if resultingLength < currentLength {
setLength(resultingLength)
}
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.replaceBytes(in: range, withBytes: replacementBytes, length: replacementLength)
_backing = .mutable(data)
_length = replacementLength
_bytes = data.mutableBytes
case .mutable(let d):
d.replaceBytes(in: range, withBytes: replacementBytes, length: replacementLength)
_backing = .mutable(d)
_length = replacementLength
_bytes = d.mutableBytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.replaceBytes(in: range, withBytes: replacementBytes, length: replacementLength)
_backing = .customMutableReference(data)
case .customMutableReference(let d):
d.replaceBytes(in: range, withBytes: replacementBytes, length: replacementLength)
}
}
@inline(__always)
public func resetBytes(in range: NSRange) {
if range.length == 0 { return }
switch _backing {
case .swift:
if _length < range.location + range.length {
let newLength = range.location + range.length
if _capacity < newLength {
_grow(newLength, false)
}
_length = newLength
}
memset(_bytes!.advanced(by: range.location), 0, range.length)
case .immutable(let d):
let data = d.mutableCopy() as! NSMutableData
data.resetBytes(in: range)
_backing = .mutable(data)
_length = data.length
_bytes = data.mutableBytes
case .mutable(let d):
d.resetBytes(in: range)
_length = d.length
_bytes = d.mutableBytes
case .customReference(let d):
let data = d.mutableCopy() as! NSMutableData
data.resetBytes(in: range)
_backing = .customMutableReference(data)
case .customMutableReference(let d):
d.resetBytes(in: range)
}
}
public convenience init() {
self.init(capacity: 0)
}
public init(length: Int) {
precondition(length < _DataStorage.maxSize)
var capacity = (length < 1024 * 1024 * 1024) ? length + (length >> 2) : length
if _DataStorage.vmOpsThreshold <= capacity {
capacity = NSRoundUpToMultipleOfPageSize(capacity)
}
let clear = _DataStorage.shouldAllocateCleared(length)
_bytes = _DataStorage.allocate(capacity, clear)!
_capacity = capacity
_needToZero = !clear
_length = 0
setLength(length)
}
public init(capacity capacity_: Int) {
var capacity = capacity_
precondition(capacity < _DataStorage.maxSize)
if _DataStorage.vmOpsThreshold <= capacity {
capacity = NSRoundUpToMultipleOfPageSize(capacity)
}
_length = 0
_bytes = _DataStorage.allocate(capacity, false)!
_capacity = capacity
_needToZero = true
}
public init(bytes: UnsafeRawPointer?, length: Int) {
precondition(length < _DataStorage.maxSize)
if length == 0 {
_capacity = 0
_length = 0
_needToZero = false
_bytes = nil
} else if _DataStorage.vmOpsThreshold <= length {
_capacity = length
_length = length
_needToZero = true
_bytes = _DataStorage.allocate(length, false)!
_DataStorage.move(_bytes!, bytes, length)
} else {
var capacity = length
if _DataStorage.vmOpsThreshold <= capacity {
capacity = NSRoundUpToMultipleOfPageSize(capacity)
}
_length = length
_bytes = _DataStorage.allocate(capacity, false)!
_capacity = capacity
_needToZero = true
_DataStorage.move(_bytes!, bytes, length)
}
}
public init(bytes: UnsafeMutableRawPointer?, length: Int, copy: Bool, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)?) {
precondition(length < _DataStorage.maxSize)
if length == 0 {
_capacity = 0
_length = 0
_needToZero = false
_bytes = nil
if let dealloc = deallocator,
let bytes_ = bytes {
dealloc(bytes_, length)
}
} else if !copy {
_capacity = length
_length = length
_needToZero = false
_bytes = bytes
_deallocator = deallocator
} else if _DataStorage.vmOpsThreshold <= length {
_capacity = length
_length = length
_needToZero = true
_bytes = _DataStorage.allocate(length, false)!
_DataStorage.move(_bytes!, bytes, length)
if let dealloc = deallocator {
dealloc(bytes!, length)
}
} else {
var capacity = length
if _DataStorage.vmOpsThreshold <= capacity {
capacity = NSRoundUpToMultipleOfPageSize(capacity)
}
_length = length
_bytes = _DataStorage.allocate(capacity, false)!
_capacity = capacity
_needToZero = true
_DataStorage.move(_bytes!, bytes, length)
if let dealloc = deallocator {
dealloc(bytes!, length)
}
}
}
public init(immutableReference: NSData) {
_bytes = UnsafeMutableRawPointer(mutating: immutableReference.bytes)
_capacity = 0
_needToZero = false
_length = immutableReference.length
_backing = .immutable(immutableReference)
}
public init(mutableReference: NSMutableData) {
_bytes = mutableReference.mutableBytes
_capacity = 0
_needToZero = false
_length = mutableReference.length
_backing = .mutable(mutableReference)
}
public init(customReference: NSData) {
_bytes = nil
_capacity = 0
_needToZero = false
_length = 0
_backing = .customReference(customReference)
}
public init(customMutableReference: NSMutableData) {
_bytes = nil
_capacity = 0
_needToZero = false
_length = 0
_backing = .customMutableReference(customMutableReference)
}
deinit {
switch _backing {
case .swift:
_freeBytes()
default:
break
}
}
@inline(__always)
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)
case .immutable(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
}
case .mutable(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
}
case .customReference(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
}
case .customMutableReference(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return _DataStorage(mutableReference: d.mutableCopy() as! NSMutableData)
} else {
return _DataStorage(mutableReference: d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC().mutableCopy() as! NSMutableData)
}
}
}
public func withInteriorPointerReference<T>(_ range: Range<Int>, _ work: (NSData) throws -> T) rethrows -> T {
switch _backing {
case .swift:
let d = _bytes == nil ? NSData() : NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false)
return try work(d)
case .immutable(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return try work(d)
} else {
return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
}
case .mutable(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return try work(d)
} else {
return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
}
case .customReference(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return try work(d)
} else {
return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
}
case .customMutableReference(let d):
if range.lowerBound == 0 && range.upperBound == _length {
return try work(d)
} else {
return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
}
}
}
public func bridgedReference() -> NSData {
switch _backing {
case .swift:
return _NSSwiftData(backing: self)
case .immutable(let d):
return d
case .mutable(let d):
return d
case .customReference(let d):
return d
case .customMutableReference(let d):
// Because this is returning an object that may be mutated in the future it needs to create a copy to prevent
// any further mutations out from under the receiver
return d.copy() as! NSData
}
}
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))
case .customMutableReference(let d):
return d.subdata(with: NSRange(location: range.lowerBound, length: range.count))
default:
return Data(bytes: _bytes!.advanced(by: range.lowerBound), count: range.count)
}
}
}
internal class _NSSwiftData : NSData {
var _backing: _DataStorage!
convenience init(backing: _DataStorage) {
self.init()
_backing = backing
}
override var length: Int {
return _backing.length
}
override var bytes: UnsafeRawPointer {
// NSData's byte pointer methods are not annotated for nullability correctly
// (but assume non-null by the wrapping macro guards). This placeholder value
// is to work-around this bug. Any indirection to the underlying bytes of an NSData
// with a length of zero would have been a programmer error anyhow so the actual
// return value here is not needed to be an allocated value. This is specifically
// needed to live like this to be source compatible with Swift3. Beyond that point
// this API may be subject to correction.
return _backing.bytes ?? UnsafeRawPointer(bitPattern: 0xBAD0)!
}
override func copy(with zone: NSZone? = nil) -> Any {
return NSData(bytes: _backing.bytes, length: _backing.length)
}
#if !DEPLOYMENT_RUNTIME_SWIFT
@objc
func _isCompact() -> Bool {
return true
}
#endif
#if DEPLOYMENT_RUNTIME_SWIFT
override func _providesConcreteBacking() -> Bool {
return true
}
#else
@objc(_providesConcreteBacking)
func _providesConcreteBacking() -> Bool {
return true
}
#endif
}
public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessCollection, MutableCollection, RangeReplaceableCollection {
public typealias ReferenceType = NSData
public typealias ReadingOptions = NSData.ReadingOptions
public typealias WritingOptions = NSData.WritingOptions
public typealias SearchOptions = NSData.SearchOptions
public typealias Base64EncodingOptions = NSData.Base64EncodingOptions
public typealias Base64DecodingOptions = NSData.Base64DecodingOptions
public typealias Index = Int
public typealias Indices = CountableRange<Int>
@_versioned internal var _backing : _DataStorage
@_versioned internal var _sliceRange: Range<Index>
// A standard or custom deallocator for `Data`.
///
/// When creating a `Data` with the no-copy initializer, you may specify a `Data.Deallocator` to customize the behavior of how the backing store is deallocated.
public enum Deallocator {
/// Use a virtual memory deallocator.
#if !DEPLOYMENT_RUNTIME_SWIFT
case virtualMemory
#endif
/// Use `munmap`.
case unmap
/// Use `free`.
case free
/// Do nothing upon deallocation.
case none
/// A custom deallocator.
case custom((UnsafeMutableRawPointer, Int) -> Void)
fileprivate var _deallocator : ((UnsafeMutableRawPointer, Int) -> Void) {
#if DEPLOYMENT_RUNTIME_SWIFT
switch self {
case .unmap:
return { __NSDataInvokeDeallocatorUnmap($0, $1) }
case .free:
return { __NSDataInvokeDeallocatorFree($0, $1) }
case .none:
return { _, _ in }
case .custom(let b):
return { (ptr, len) in
b(ptr, len)
}
}
#else
switch self {
case .virtualMemory:
return { NSDataDeallocatorVM($0, UInt($1)) }
case .unmap:
return { NSDataDeallocatorUnmap($0, UInt($1)) }
case .free:
return { NSDataDeallocatorFree($0, UInt($1)) }
case .none:
return { _, _ in }
case .custom(let b):
return { (ptr, len) in
b(ptr, len)
}
}
#endif
}
}
// MARK: -
// MARK: Init methods
/// Initialize a `Data` with copied memory content.
///
/// - parameter bytes: A pointer to the memory. It will be copied.
/// - parameter count: The number of bytes to copy.
public init(bytes: UnsafeRawPointer, count: Int) {
_backing = _DataStorage(bytes: bytes, length: count)
_sliceRange = 0..<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<SourceType>(buffer: UnsafeBufferPointer<SourceType>) {
let count = MemoryLayout<SourceType>.stride * buffer.count
_backing = _DataStorage(bytes: buffer.baseAddress, length: count)
_sliceRange = 0..<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<SourceType>(buffer: UnsafeMutableBufferPointer<SourceType>) {
let count = MemoryLayout<SourceType>.stride * buffer.count
_backing = _DataStorage(bytes: buffer.baseAddress, length: count)
_sliceRange = 0..<count
}
/// Initialize a `Data` with the contents of an Array.
///
/// - parameter bytes: An array of bytes to copy.
public init(bytes: Array<UInt8>) {
let count = bytes.count
_backing = bytes.withUnsafeBufferPointer {
return _DataStorage(bytes: $0.baseAddress, length: count)
}
_sliceRange = 0..<count
}
/// Initialize a `Data` with the contents of an Array.
///
/// - parameter bytes: An array of bytes to copy.
public init(bytes: ArraySlice<UInt8>) {
let count = bytes.count
_backing = bytes.withUnsafeBufferPointer {
return _DataStorage(bytes: $0.baseAddress, length: count)
}
_sliceRange = 0..<count
}
/// Initialize a `Data` with a repeating byte pattern
///
/// - parameter repeatedValue: A byte to initialize the pattern
/// - parameter count: The number of bytes the data initially contains initialized to the repeatedValue
public init(repeating repeatedValue: UInt8, count: Int) {
self.init(count: count)
withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
memset(bytes, Int32(repeatedValue), count)
}
}
/// Initialize a `Data` with the specified size.
///
/// This initializer doesn't necessarily allocate the requested memory right away. `Data` allocates additional memory as needed, so `capacity` simply establishes the initial capacity. When it does allocate the initial memory, though, it allocates the specified amount.
///
/// This method sets the `count` of the data to 0.
///
/// If the capacity specified in `capacity` is greater than four memory pages in size, this may round the amount of requested memory up to the nearest full page.
///
/// - parameter capacity: The size of the data.
public init(capacity: Int) {
_backing = _DataStorage(capacity: capacity)
_sliceRange = 0..<0
}
/// Initialize a `Data` with the specified count of zeroed bytes.
///
/// - parameter count: The number of bytes the data initially contains.
public init(count: Int) {
_backing = _DataStorage(length: count)
_sliceRange = 0..<count
}
/// Initialize an empty `Data`.
public init() {
_backing = _DataStorage(length: 0)
_sliceRange = 0..<0
}
/// Initialize a `Data` without copying the bytes.
///
/// If the result is mutated and is not a unique reference, then the `Data` will still follow copy-on-write semantics. In this case, the copy will use its own deallocator. Therefore, it is usually best to only use this initializer when you either enforce immutability with `let` or ensure that no other references to the underlying data are formed.
/// - parameter bytes: A pointer to the bytes.
/// - parameter count: The size of the bytes.
/// - 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)
_sliceRange = 0..<count
}
/// Initialize a `Data` with the contents of a `URL`.
///
/// - parameter url: The `URL` to read.
/// - parameter options: Options for the read operation. Default value is `[]`.
/// - 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)
_sliceRange = 0..<d.length
}
/// Initialize a `Data` from a Base-64 encoded String using the given options.
///
/// 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 `[]`.
public init?(base64Encoded base64String: String, options: Data.Base64DecodingOptions = []) {
if let d = NSData(base64Encoded: base64String, options: Base64DecodingOptions(rawValue: options.rawValue)) {
_backing = _DataStorage(immutableReference: d)
_sliceRange = 0..<d.length
} else {
return nil
}
}
/// Initialize a `Data` from a Base-64, UTF-8 encoded `Data`.
///
/// Returns nil when the input is not recognized as valid Base-64.
///
/// - parameter base64Data: Base-64, UTF-8 encoded input data.
/// - 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)
_sliceRange = 0..<d.length
} else {
return nil
}
}
/// 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) {
#if DEPLOYMENT_RUNTIME_SWIFT
let providesConcreteBacking = reference._providesConcreteBacking()
#else
let providesConcreteBacking = (reference as AnyObject)._providesConcreteBacking?() ?? false
#endif
if providesConcreteBacking {
_backing = _DataStorage(immutableReference: reference.copy() as! NSData)
_sliceRange = 0..<reference.length
} else {
_backing = _DataStorage(customReference: reference.copy() as! NSData)
_sliceRange = 0..<reference.length
}
}
@_versioned
internal init(backing: _DataStorage, range: Range<Index>) {
_backing = backing
_sliceRange = range
}
// -----------------------------------
// MARK: - Properties and Functions
/// The number of bytes in the data.
public var count: Int {
@inline(__always)
get {
return _sliceRange.count
}
@inline(__always)
set {
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.length = newValue
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + newValue)
}
}
/// Access the bytes in the data.
///
/// - 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 ?? UnsafeRawPointer(bitPattern: 0xBAD0)!
let contentPtr = bytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout<ContentType>.stride)
return try body(contentPtr)
}
/// Mutate the bytes in the data.
///
/// This function assumes that you are mutating the contents.
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
@inline(__always)
public mutating func withUnsafeMutableBytes<ResultType, ContentType>(_ body: (UnsafeMutablePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
let mutableBytes = _backing.mutableBytes ?? UnsafeMutableRawPointer(bitPattern: 0xBAD0)!
let contentPtr = mutableBytes.bindMemory(to: ContentType.self, capacity: count / MemoryLayout<ContentType>.stride)
return try body(UnsafeMutablePointer(contentPtr))
}
// MARK: -
// MARK: Copy Bytes
/// Copy the contents of the data to a pointer.
///
/// - parameter pointer: A pointer to the buffer you wish to copy the bytes into.
/// - parameter count: The number of bytes to copy.
/// - 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) {
if count == 0 { return }
memcpy(UnsafeMutableRawPointer(pointer), _backing.bytes!.advanced(by: _sliceRange.lowerBound), 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 + _sliceRange.lowerBound), range.length)
}
/// Copy a subset of the contents of the data to a pointer.
///
/// - parameter pointer: A pointer to the buffer you wish to copy the bytes into.
/// - 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<UInt8>, from range: Range<Index>) {
_copyBytesHelper(to: pointer, from: NSRange(range))
}
// Copy the contents of the data into a buffer.
///
/// This function copies the bytes in `range` from the data into the buffer. If the count of the `range` is greater than `MemoryLayout<DestinationType>.stride * buffer.count` then the first N bytes will be copied into the buffer.
/// - precondition: The range must be within the bounds of the data. Otherwise `fatalError` is called.
/// - parameter buffer: A buffer to copy the data into.
/// - parameter range: A range in the data to copy into the buffer. If the range is empty, this function will return 0 without copying anything. If the range is nil, as much data as will fit into `buffer` is copied.
/// - returns: Number of bytes copied into the destination buffer.
public func copyBytes<DestinationType>(to buffer: UnsafeMutableBufferPointer<DestinationType>, from range: Range<Index>? = nil) -> Int {
let cnt = count
guard cnt > 0 else { return 0 }
let copyRange : Range<Index>
if let r = range {
guard !r.isEmpty else { return 0 }
precondition(r.lowerBound >= 0)
precondition(r.lowerBound < cnt, "The range is outside the bounds of the data")
precondition(r.upperBound >= 0)
precondition(r.upperBound <= cnt, "The range is outside the bounds of the data")
copyRange = r.lowerBound..<(r.lowerBound + Swift.min(buffer.count * MemoryLayout<DestinationType>.stride, r.count))
} else {
copyRange = 0..<Swift.min(buffer.count * MemoryLayout<DestinationType>.stride, cnt)
}
guard !copyRange.isEmpty else { return 0 }
let nsRange = NSMakeRange(copyRange.lowerBound, copyRange.upperBound - copyRange.lowerBound)
_copyBytesHelper(to: buffer.baseAddress!, from: nsRange)
return copyRange.count
}
// MARK: -
#if !DEPLOYMENT_RUNTIME_SWIFT
@inline(__always)
private func _shouldUseNonAtomicWriteReimplementation(options: Data.WritingOptions = []) -> Bool {
// Avoid a crash that happens on OS X 10.11.x and iOS 9.x or before when writing a bridged Data non-atomically with Foundation's standard write() implementation.
if !options.contains(.atomic) {
#if os(OSX)
return NSFoundationVersionNumber <= Double(NSFoundationVersionNumber10_11_Max)
#else
return NSFoundationVersionNumber <= Double(NSFoundationVersionNumber_iOS_9_x_Max)
#endif
} else {
return false
}
}
#endif
/// Write the contents of the `Data` to a location.
///
/// - parameter url: The location to write the data into.
/// - parameter options: Options for writing the data. Default value is `[]`.
/// - throws: An error in the Cocoa domain, if there is an error writing to the `URL`.
public func write(to url: URL, options: Data.WritingOptions = []) throws {
try _backing.withInteriorPointerReference(_sliceRange) {
#if DEPLOYMENT_RUNTIME_SWIFT
try $0.write(to: url, options: WritingOptions(rawValue: options.rawValue))
#else
if _shouldUseNonAtomicWriteReimplementation(options: options) {
var error: NSError? = nil
guard __NSDataWriteToURL($0, url as NSURL, options.rawValue, &error) else { throw error! }
} else {
try $0.write(to: url, options: WritingOptions(rawValue: options.rawValue))
}
#endif
}
}
// MARK: -
/// Find the given `Data` in the content of this `Data`.
///
/// - parameter dataToFind: The data to be searched for.
/// - parameter options: Options for the search. Default value is `[]`.
/// - parameter range: The range of this data in which to perform the search. Default value is `nil`, which means the entire content of this data.
/// - returns: A `Range` specifying the location of the found data, or nil if a match could not be found.
/// - precondition: `range` must be in the bounds of the Data.
public func range(of dataToFind: Data, options: Data.SearchOptions = [], in range: Range<Index>? = nil) -> Range<Index>? {
let nsRange : NSRange
if let r = range {
nsRange = NSMakeRange(r.lowerBound, r.upperBound - r.lowerBound)
} else {
nsRange = NSMakeRange(0, _backing.length)
}
let result = _backing.withInteriorPointerReference(_sliceRange) {
$0.range(of: dataToFind, options: options, in: nsRange)
}
if result.location == NSNotFound {
return nil
}
return result.location..<(result.location + result.length)
}
/// Enumerate the contents of the data.
///
/// 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)
}
@inline(__always)
public mutating func append(_ bytes: UnsafePointer<UInt8>, count: Int) {
if count == 0 { return }
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.append(bytes, length: count)
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.upperBound + count)
}
@inline(__always)
public mutating func append(_ other: Data) {
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.append(other._backing)
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.upperBound + other.count)
}
/// Append a buffer of bytes to the data.
///
/// - parameter buffer: The buffer of bytes to append. The size is calculated from `SourceType` and `buffer.count`.
@inline(__always)
public mutating func append<SourceType>(_ buffer : UnsafeBufferPointer<SourceType>) {
if buffer.count == 0 { return }
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.append(buffer.baseAddress!, length: buffer.count * MemoryLayout<SourceType>.stride)
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.upperBound + buffer.count * MemoryLayout<SourceType>.stride)
}
@inline(__always)
public mutating func append(_ other: MutableRangeReplaceableRandomAccessSlice<Data>) {
let count = other.count
if count == 0 { return }
other.base.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
append(bytes, count: count)
}
}
@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
}
self[idx] = byte
idx = newIndex
}
}
@inline(__always)
public mutating func append(contentsOf bytes: [UInt8]) {
bytes.withUnsafeBufferPointer { (buffer: UnsafeBufferPointer<UInt8>) -> Void in
append(buffer)
}
}
// MARK: -
/// Set a region of the data to `0`.
///
/// If `range` exceeds the bounds of the data, then the data is resized to fit.
/// - parameter range: The range in the data to set to `0`.
@inline(__always)
public mutating func resetBytes(in range: Range<Index>) {
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)
}
}
/// 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 data: The replacement data.
@inline(__always)
public mutating func replaceSubrange(_ subrange: Range<Index>, with data: Data) {
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
let cnt = data.count
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
let currentLength = _backing.length
_backing.replaceBytes(in: nsRange, with: bytes, length: cnt)
let resultingLength = currentLength - nsRange.length + cnt
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
}
}
/// 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.
@inline(__always)
public mutating func replaceSubrange<SourceType>(_ subrange: Range<Index>, with buffer: UnsafeBufferPointer<SourceType>) {
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)
}
/// 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.
@inline(__always)
public mutating func replaceSubrange<ByteCollection : Collection>(_ subrange: Range<Index>, 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<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")
}
}
}
@inline(__always)
public mutating func replaceSubrange(_ subrange: Range<Index>, with bytes: UnsafeRawPointer, count cnt: Int) {
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
let currentLength = _backing.length
_backing.replaceBytes(in: nsRange, with: bytes, length: cnt)
let resultingLength = currentLength - nsRange.length + cnt
_sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
}
/// Return a new copy of the data in a specified range.
///
/// - parameter range: The range to copy.
@inline(__always)
public func subdata(in range: Range<Index>) -> Data {
let length = count
if count == 0 {
return Data()
}
precondition(length >= range.upperBound)
return _backing.subdata(in: range)
}
// MARK: -
//
/// Returns a Base-64 encoded string.
///
/// - 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 _backing.withInteriorPointerReference(_sliceRange) {
return $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 _backing.withInteriorPointerReference(_sliceRange) {
return $0.base64EncodedData(options: options)
}
}
// MARK: -
//
/// The hash value for the data.
public var hashValue: Int {
return _backing.hashValue
}
@inline(__always)
public func advanced(by amount: Int) -> Data {
let length = count - amount
precondition(length > 0)
return withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> Data in
return Data(bytes: ptr.advanced(by: amount), count: length)
}
}
// MARK: -
// MARK: -
// MARK: Index and Subscript
/// Sets or returns the byte at the specified index.
public subscript(index: Index) -> UInt8 {
@inline(__always)
get {
return withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> UInt8 in
return bytes.advanced(by: index).pointee
}
}
@inline(__always)
set {
if !isKnownUniquelyReferenced(&_backing) {
_backing = _backing.mutableCopy(_sliceRange)
}
_backing.set(index, to: newValue)
}
}
public subscript(bounds: Range<Index>) -> Data {
@inline(__always)
get {
return Data(backing: _backing, range: bounds)
}
@inline(__always)
set {
replaceSubrange(bounds, with: newValue)
}
}
/// The start `Index` in the data.
public var startIndex: Index {
@inline(__always)
get {
return _sliceRange.lowerBound
}
}
/// The end `Index` into the data.
///
/// This is the "one-past-the-end" position, and will always be equal to the `count`.
public var endIndex: Index {
@inline(__always)
get {
return _sliceRange.upperBound
}
}
@inline(__always)
public func index(before i: Index) -> Index {
return i - 1
}
@inline(__always)
public func index(after i: Index) -> Index {
return i + 1
}
public var indices: CountableRange<Int> {
@inline(__always)
get {
return startIndex..<endIndex
}
}
/// An iterator over the contents of the data.
///
/// The iterator will increment byte-by-byte.
public func makeIterator() -> Data.Iterator {
return Iterator(_data: self)
}
public struct Iterator : IteratorProtocol {
private let _data: Data
private var _buffer: (
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
private var _idx: Data.Index
private let _endIdx: Data.Index
fileprivate init(_data: Data) {
self._data = _data
_buffer = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
_idx = _data.startIndex
_endIdx = _data.endIndex
}
public mutating func next() -> UInt8? {
guard _idx < _endIdx else { return nil }
defer { _idx += 1 }
let bufferSize = MemoryLayout.size(ofValue: _buffer)
return withUnsafeMutablePointer(to: &_buffer) { ptr_ in
let ptr = UnsafeMutableRawPointer(ptr_).assumingMemoryBound(to: UInt8.self)
let bufferIdx = (_idx - _data.startIndex) % bufferSize
if bufferIdx == 0 {
// populate the buffer
_data.copyBytes(to: ptr, from: _idx..<(_endIdx - _idx > bufferSize ? _idx + bufferSize : _endIdx))
}
return ptr[bufferIdx]
}
}
}
// MARK: -
//
@available(*, unavailable, renamed: "count")
public var length: Int {
get { fatalError() }
set { fatalError() }
}
@available(*, unavailable, message: "use withUnsafeBytes instead")
public var bytes: UnsafeRawPointer { fatalError() }
@available(*, unavailable, message: "use withUnsafeMutableBytes instead")
public var mutableBytes: UnsafeMutableRawPointer { fatalError() }
/// Returns `true` if the two `Data` arguments are equal.
public static func ==(d1 : Data, d2 : Data) -> Bool {
let backing1 = d1._backing
let backing2 = d2._backing
if backing1 === backing2 {
return true
}
let length1 = backing1.length
if length1 != backing2.length {
return false
}
if backing1.bytes == backing2.bytes {
return true
}
if length1 > 0 {
return d1.withUnsafeBytes { (b1) in
return d2.withUnsafeBytes { (b2) in
return memcmp(b1, b2, length1) == 0
}
}
}
return true
}
}
extension Data : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable {
/// A human-readable description for the data.
public var description: String {
return "\(self.count) bytes"
}
/// A human-readable debug description for the data.
public var debugDescription: String {
return self.description
}
public var customMirror: Mirror {
let nBytes = self.count
var children: [(label: String?, value: Any)] = []
children.append((label: "count", value: nBytes))
self.withUnsafeBytes { (bytes : UnsafePointer<UInt8>) in
children.append((label: "pointer", value: bytes))
}
// Minimal size data is output as an array
if nBytes < 64 {
children.append((label: "bytes", value: Array(self[0..<nBytes])))
}
let m = Mirror(self, children:children, displayStyle: Mirror.DisplayStyle.struct)
return m
}
}
extension Data {
@available(*, unavailable, renamed: "copyBytes(to:count:)")
public func getBytes<UnsafeMutablePointerVoid: _Pointer>(_ buffer: UnsafeMutablePointerVoid, length: Int) { }
@available(*, unavailable, renamed: "copyBytes(to:from:)")
public func getBytes<UnsafeMutablePointerVoid: _Pointer>(_ buffer: UnsafeMutablePointerVoid, range: NSRange) { }
}
/// Provides bridging functionality for struct Data to class NSData and vice-versa.
#if DEPLOYMENT_RUNTIME_SWIFT
internal typealias DataBridgeType = _ObjectTypeBridgeable
#else
internal typealias DataBridgeType = _ObjectiveCBridgeable
#endif
extension Data : DataBridgeType {
@_semantics("convertToObjectiveC")
public func _bridgeToObjectiveC() -> NSData {
return _backing.bridgedReference()
}
public static func _forceBridgeFromObjectiveC(_ input: NSData, result: inout Data?) {
// We must copy the input because it might be mutable; just like storing a value type in ObjC
result = Data(referencing: input)
}
public static func _conditionallyBridgeFromObjectiveC(_ input: NSData, result: inout Data?) -> Bool {
// We must copy the input because it might be mutable; just like storing a value type in ObjC
result = Data(referencing: input)
return true
}
public static func _unconditionallyBridgeFromObjectiveC(_ source: NSData?) -> Data {
var result: Data?
_forceBridgeFromObjectiveC(source!, result: &result)
return result!
}
}
extension NSData : _HasCustomAnyHashableRepresentation {
// Must be @nonobjc to avoid infinite recursion during bridging.
@nonobjc
public func _toCustomAnyHashable() -> AnyHashable? {
return AnyHashable(Data._unconditionallyBridgeFromObjectiveC(self))
}
}