Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active January 30, 2019 00:40
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rnapier/da148690af63c401097d to your computer and use it in GitHub Desktop.
Save rnapier/da148690af63c401097d to your computer and use it in GitHub Desktop.
Bytes collection for NSData
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