mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Add RangeReplaceableCollection conformance to struct Data.
This also addresses a TODO in the code, now that we can access the base of a slice.
This commit is contained in:
@@ -66,7 +66,7 @@ internal final class _SwiftNSData : _SwiftNativeNSData, _SwiftNativeFoundationTy
|
||||
|
||||
`Data` can be initialized with an `UnsafePointer` and count, an array of `UInt8` (the primitive byte type), or an `UnsafeBufferPointer`. The buffer-oriented functions provide an extra measure of safety by automatically performing the size calculation, as the type is known at compile time.
|
||||
*/
|
||||
public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, Hashable, RandomAccessCollection, MutableCollection, _MutablePairBoxing {
|
||||
public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, Hashable, RandomAccessCollection, MutableCollection, RangeReplaceableCollection, _MutablePairBoxing {
|
||||
/// The Objective-C bridged type of `Data`.
|
||||
public typealias ReferenceType = NSData
|
||||
|
||||
@@ -163,29 +163,29 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
|
||||
|
||||
/// Initialize a `Data` with the specified size.
|
||||
///
|
||||
/// This initializer doesn't necessarily allocate the requested memory right away. Mutable 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 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) {
|
||||
public init(capacity: Int) {
|
||||
if let d = NSMutableData(capacity: capacity) {
|
||||
_wrapped = _SwiftNSData(mutableObject: d)
|
||||
} else {
|
||||
return nil
|
||||
fatalError("Unable to allocate data of the requested capacity")
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize a `Data` with the specified count of zeroed bytes.
|
||||
///
|
||||
/// - parameter count: The number of bytes the data initially contains.
|
||||
public init?(count: Int) {
|
||||
public init(count: Int) {
|
||||
if let d = NSMutableData(length: count) {
|
||||
_wrapped = _SwiftNSData(mutableObject: d)
|
||||
} else {
|
||||
return nil
|
||||
fatalError("Unable to allocate data of the requested count")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,10 +249,10 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
|
||||
/// 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(reference: NSData) {
|
||||
public init(referencing reference: NSData) {
|
||||
_wrapped = _SwiftNSData(immutableObject: reference.copy())
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
// MARK: - Properties and Functions
|
||||
|
||||
@@ -461,11 +461,11 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
|
||||
///
|
||||
/// This will resize the data if required, to fit the entire contents of `data`.
|
||||
///
|
||||
/// - precondition: `range` must be within the range of the data.
|
||||
/// - parameter range: The range in the data to replace.
|
||||
/// - precondition: The bounds of `subrange` must be valid indicies 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.
|
||||
public mutating func replaceBytes(in range: Range<Index>, with data: Data) {
|
||||
let nsRange = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound)
|
||||
public mutating func replaceSubrange(_ subrange: Range<Index>, with data: Data) {
|
||||
let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
|
||||
let cnt = data.count
|
||||
let bytes = data._getUnsafeBytesPointer()
|
||||
|
||||
@@ -478,10 +478,11 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
|
||||
///
|
||||
/// This will resize the data if required, to fit the entire contents of `buffer`.
|
||||
///
|
||||
/// - precondition: `range` must be within the range of the data.
|
||||
/// - precondition: The bounds of `subrange` must be valid indicies of the collection.
|
||||
/// - parameter subrange: The range in the data to replace.
|
||||
/// - parameter buffer: The replacement bytes.
|
||||
public mutating func replaceBytes<SourceType>(in range: Range<Index>, with buffer: UnsafeBufferPointer<SourceType>) {
|
||||
let nsRange = NSMakeRange(range.lowerBound, range.upperBound - range.lowerBound)
|
||||
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 * strideof(SourceType.self)
|
||||
|
||||
_applyUnmanagedMutation {
|
||||
@@ -490,6 +491,52 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
|
||||
|
||||
}
|
||||
|
||||
/// 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 indicies of the collection.
|
||||
/// - parameter subrange: The range in the data to replace.
|
||||
/// - parameter newElements: The replacement bytes.
|
||||
public mutating func replaceSubrange<ByteCollection : Collection where ByteCollection.Iterator.Element == Data.Iterator.Element>(_ subrange: Range<Index>, with newElements: ByteCollection) {
|
||||
|
||||
// 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>) -> () in
|
||||
if shift != 0 {
|
||||
let destination = bytes + start + replacementCount
|
||||
let source = bytes + start + subrangeCount
|
||||
memmove(destination, source, currentCount - start - subrangeCount)
|
||||
}
|
||||
|
||||
if replacementCount != 0 {
|
||||
newElements._copyContents(initializing: bytes + start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Return a new copy of the data in a specified range.
|
||||
///
|
||||
/// - parameter range: The range to copy.
|
||||
@@ -561,15 +608,7 @@ public struct Data : ReferenceConvertible, CustomStringConvertible, Equatable, H
|
||||
return MutableRandomAccessSlice(base: self, bounds: bounds)
|
||||
}
|
||||
set {
|
||||
// Ideally this would be:
|
||||
// replaceBytes(in: bounds, with: newValue._base)
|
||||
// but we do not have access to _base due to 'internal' protection
|
||||
// TODO: Use a custom Slice type so we have access to the underlying data
|
||||
let arrayOfBytes = newValue.map { $0 }
|
||||
arrayOfBytes.withUnsafeBufferPointer {
|
||||
let otherData = Data(buffer: $0)
|
||||
replaceBytes(in: bounds, with: otherData)
|
||||
}
|
||||
replaceSubrange(bounds, with: newValue.base)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,12 +658,12 @@ extension Data : _ObjectiveCBridgeable {
|
||||
|
||||
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(reference: input)
|
||||
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(reference: input)
|
||||
result = Data(referencing: input)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user