Last active
January 30, 2019 00:40
-
-
Save rnapier/da148690af63c401097d to your computer and use it in GitHub Desktop.
Bytes collection for NSData
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// Why bytesView rather than just extending NSData directly? | |
// Because this way we can keep our extension internal and not conflict | |
// with someone who imports us and has also extended NSData. | |
// If you're top-level code, you can just hoist everyting up to NSData directly. | |
internal extension NSData { | |
var bytesView: BytesView { return BytesView(self) } | |
} | |
struct BytesView: CollectionType { | |
// The view retains the NSData. That's on purpose. NSData doesn't retain the view, so there's no loop. | |
let data: NSData | |
init(_ data: NSData) { self.data = data } | |
subscript (position: Int) -> UInt8 { | |
return UnsafePointer<UInt8>(data.bytes)[position] | |
} | |
subscript (bounds: Range<Int>) -> NSData { | |
return data.subdataWithRange(NSRange(bounds)) | |
} | |
var startIndex: Int = 0 | |
var endIndex: Int { return data.length } | |
} | |
internal extension NSMutableData { | |
var mutableBytesView: MutableBytesView { return MutableBytesView(self) } | |
} | |
final class MutableBytesView: RangeReplaceableCollectionType { | |
let data: NSMutableData | |
init() { self.data = NSMutableData() } | |
init(_ data: NSMutableData) { self.data = data } | |
subscript (position: Int) -> UInt8 { | |
get { | |
return UnsafePointer<UInt8>(data.bytes)[position] | |
} | |
set { | |
replaceRange(position...position, with: [newValue]) | |
} | |
} | |
subscript (bounds: Range<Int>) -> NSData { | |
get { | |
return data.subdataWithRange(NSRange(bounds)) | |
} | |
set { | |
replaceRange(bounds, with: newValue.bytesView) | |
} | |
} | |
var startIndex: Int = 0 | |
var endIndex: Int { return data.length } | |
// You could optimize here by specializing NSData and [UInt8] to avoid copying. | |
// But this is the most general implementation. | |
func replaceRange<C : CollectionType where C.Generator.Element == UInt8>(subRange: Range<Int>, with newElements: C) { | |
let newElements = Array(newElements) | |
data.replaceBytesInRange(NSRange(subRange), withBytes: newElements, length: newElements.count) | |
} | |
// And of course you can optimize here by implementing append and the like (though it may not optimize much). | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment