What does pointee property do in swift? - swift

let data = Data()
data.withUnsafeBytes { (uPtr: UnsafePointer<UInt8>) in
var ptr = uPtr
var j = ptr.pointee
var new = ptr.advanced(by: 1)
}
What does j represent or store here, is it pointing to the next byte
of the data? Will new variable store the next byte? Any Link To Learn Pointer in A Simpler Way In Swift And What Are the Advantages of using pointer in Swift?

pointee is the value being stored and pointed to by the pointer. It used to be called memory in earlier versions of Swift. Don't use pointers in Swift unless you absolutely must, because it involves you in retaining and releasing memory manually:
UnsafePointer provides no automated memory management or alignment
guarantees. You are responsible for handling the life cycle of any
memory you work with through unsafe pointers to avoid leaks or
undefined behavior.
In your example you have no data but if you were to have data then the first pointer would first of all point to the memory address of the first value in the data array, e.g.
var str = "Hello, playground"
guard let data = str.data(using: .utf8) else {fatalError()}
data.withUnsafeBytes { (uPtr: UnsafePointer<UInt8>) in
let ptr = uPtr
ptr.pointee // 72
let ptr2 = ptr.advanced(by: 1)
ptr2.pointee // 101
}
So here we'd expect ptr.pointee to be the utf8 value of "H" which is 72, and when we advance by 1 we then have a pointer to the value of "e" which is 101, i.e. we are working our way through the Array. But you wouldn't want to actually do this because it assumes that the memory addresses for each item in the array are contiguous, which they might not be.
A more sober approach to retrieving the bytes would be:
data.withUnsafeBytes { [UInt8](UnsafeBufferPointer(start:$0, count:data.count))
}
or better still:
[UInt8](data)
As outlined in this post.

Related

Errors when update code to avoid deprecation warnings withUnsafeMutableBytes in swift 5

I've updated to swift 5 and one of the dependencies I use won't compile in swift 5. I've fixed it, but now I'm getting 350+ deprecation warnings all over the file. They're all similar to this:
withUnsafeMutableBytes is deprecated: use withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R instead
And this is a snipit of the code (it's basically just calling a c library's functions):
var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { kPtr in
flutter_sodium.crypto_generichash_keygen(kPtr)
}
For reference, in the above crypto_generichash_keybytes() just returns a size_t and crypto_generichash_keygen's signature is void crypto_generichash_keygen(unsigned char k[crypto_generichash_KEYBYTES]);.
I figured out (as this answer states) that the way to get around this should be to call kPtr.baseAddress:
var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { kPtr in
flutter_sodium.crypto_generichash_keygen(kPtr.baseAddress)
}
as that should use the withUnsafeMutableBytes<ResultType> variant rather than the deprecated withUnsafeMutableBytes<ResultType, ContentType>. However, this instead results in the error
value of type 'UnsafeMutablePointer<_>' has no member 'baseAddress'.
If I explicitly specify the resultType and kPtr:
var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { (kPtr: UnsafeMutableRawBufferPointer) -> Void in
flutter_sodium.crypto_generichash_keygen(kPtr.baseAddress)
}
I instead get
UnsafeMutableRawBufferPointer' is not convertible to 'UnsafeMutablePointer<_>'.
Are there any swift experts out there that can help me figure out the right way to do this? I know the warnings are just warnings, but I prefer to have code that compiles with no warnings.
I took a look at Swift 5.0: 'withUnsafeBytes' is deprecated: use `withUnsafeBytes<R>(...) before posting this question and it doesn't help my situation as I'm not loading the pointer but rather using the data. Also, I've done exactly what the documentation tells me to but that still isn't helping.
EDIT: To be a bit more clear, some of the 350+ warnings were related to code where the Data is allocated in the code, however some of them are where I receive Data from an external source. That looks something like this:
let args = call.arguments as! NSDictionary
let server_pk = (args["server_pk"] as! FlutterStandardTypedData).data
let server_sk = (args["server_sk"] as! FlutterStandardTypedData).data
let client_pk = (args["client_pk"] as! FlutterStandardTypedData).data
var rx = Data(count: flutter_sodium.crypto_kx_sessionkeybytes())
var tx = Data(count: flutter_sodium.crypto_kx_sessionkeybytes())
let ret = rx.withUnsafeMutableBytes { rxPtr in
tx.withUnsafeMutableBytes { txPtr in
server_pk.withUnsafeBytes { server_pkPtr in
server_sk.withUnsafeBytes { server_skPtr in
client_pk.withUnsafeBytes { client_pkPtr in
flutter_sodium.crypto_kx_server_session_keys(rxPtr, txPtr, server_pkPtr, server_skPtr, client_pkPtr)
}
}
}
}
}
with the corresponding method call
SODIUM_EXPORT
int crypto_kx_client_session_keys(unsigned char rx[crypto_kx_SESSIONKEYBYTES],
unsigned char tx[crypto_kx_SESSIONKEYBYTES],
const unsigned char client_pk[crypto_kx_PUBLICKEYBYTES],
const unsigned char client_sk[crypto_kx_SECRETKEYBYTES],
const unsigned char server_pk[crypto_kx_PUBLICKEYBYTES])
__attribute__ ((warn_unused_result));
(and I know that the code is not really optimal swift, but when dealing with interoperability between dart and swift this is what the flutter team came up with for how to do it).
When I asked the question I was trying to distill it down to the simplest case but that case had a specific answer which differs to the overall problem I'm having.
I wouldn't use Data here – Data represents an untyped collection of "raw" bytes, however crypto_generichash_keygen wants a mutable pointer to typed memory. The reason why the UnsafeMutablePointer<T> variant of withUnsafeMutableBytes was deprecated is that it's fundamentally the wrong abstraction to be providing on untyped memory.
The simplest way to get a buffer of typed memory in Swift is with an Array:
var k = [UInt8](repeating: 0, count: crypto_generichash_keybytes())
flutter_sodium.crypto_generichash_keygen(&k)
You can always turn the resulting buffer into a Data value afterwards by saying Data(k).
Another option is to use an UnsafeMutableBufferPointer:
let k = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: crypto_generichash_keybytes())
defer {
k.deallocate()
}
flutter_sodium.crypto_generichash_keygen(k.baseAddress!)
// Now use the buffer `k` – just make sure you finish using it before the end of
// the scope when `deallocate()` gets called!
Unlike Array, this avoids having to pre-fill the resulting buffer with zeros before being passed off to the C API, however this likely isn't of concern. But just like Array, you can turn such a buffer into a Data by just saying Data(k).
For cases where you get handed a Data value from some external source and need to pass it off to an API as a typed pointer, the simplest and safest option is to just turn it into an array before passing it by saying Array(someData).
For example:
let args = call.arguments as! NSDictionary
let server_pk = (args["server_pk"] as! FlutterStandardTypedData).data
let server_sk = (args["server_sk"] as! FlutterStandardTypedData).data
let client_pk = (args["client_pk"] as! FlutterStandardTypedData).data
var rx = [UInt8](repeating: 0, count: flutter_sodium.crypto_kx_sessionkeybytes())
var tx = [UInt8](repeating: 0, count: flutter_sodium.crypto_kx_sessionkeybytes())
flutter_sodium.crypto_kx_server_session_keys(
&rx, &tx, Array(server_pk), Array(server_sk), Array(client_pk)
)
You probably could use withUnsafeBytes and call bindMemory on the underlying pointer, but I would discourage it, as it changes the type of the underlying memory which could subtly impact the soundness of any other Swift code sharing that memory due to the fact that you're switching out the type from under it.

Reason why Unsafe Pointers are used in Swift, especially in Metal

I have been looking into potential use cases of UnsafePointer and related UnsafeX in Swift, and am wondering what the use case is i Swift. It sounds like the main use case is performance, but then at the same time types are supposed to offer compiler optimizations and so performance, so I'm not sure when they are actually useful. I would like to know if all things can be refactored to not use them with the same or better performance, or if not, what a specific example with description of the code and perhaps some code or pseudocode that demonstrates how it offers a performance advantage. I would basically like to have a reference of a specific example demoing a performance advantage of unsafe pointers and unsafe stuff.
Some things I've found related to Swift:
However, UnsafePointer is an important API for interoperability and building high performance data structures. - http://atrick.github.io/proposal/voidpointer.html
But typing allows for compiler optimizations. I'm wondering what advantages using the Unsafe features gives you.
True Unsafe Code Performance
https://nbsoftsolutions.com/blog/high-performance-unsafe-c-code-is-a-lie
https://www.reddit.com/r/csharp/comments/67oi9p/can_anyone_enlighten_me_on_why_unsafe_code_is/
https://medium.com/#vCabbage/go-are-pointers-a-performance-optimization-a95840d3ef85
Some places you see the use of this is in Metal code, such as here:
// Create buffers used in the shader
guard let uniformBuffer = device.makeBuffer(length: MemoryLayout<Uniforms>.stride) else { throw Error.failedToCreateMetalBuffer(device: device) }
uniformBuffer.label = "me.dehesa.metal.buffers.uniform"
uniformBuffer.contents().bindMemory(to: Uniforms.self, capacity: 1)
// or here
let ptr = uniformsBuffer.contents().assumingMemoryBound(to: Uniforms.self)
ptr.pointee = Uniforms(modelViewProjectionMatrix: modelViewProjectionMatrix, modelViewMatrix: modelViewMatrix, normalMatrix: normalMatrix)
I don't really understand what's going on with the pointers too well yet, but I wanted to ask to see if these use cases offer performance enhancements or if they could be refactored to use a safe version that had similar or even better performance.
Saw it here too:
func setBit(_ index: Int, value: Bool, pointer: UnsafeMutablePointer<UInt8>) {
let bit: UInt8 = value ? 0xFF : 0
pointer.pointee ^= (bit ^ pointer.pointee) & (1 << UInt8(index))
}
More metal:
uniforms = UnsafeMutableRawPointer(uniformBuffer.contents()).bindMemory(to:GUniforms.self, capacity:1)
vertexBuffer = device?.makeBuffer(length: 3 * MemoryLayout<GVertex>.stride * 6, options: .cpuCacheModeWriteCombined)
vertices = UnsafeMutableRawPointer(vertexBuffer!.contents()).bindMemory(to:GVertex.self, capacity:3)
vertexBuffer1 = device?.makeBuffer(length: maxCount * maxCount * MemoryLayout<GVertex>.stride * 4, options: .cpuCacheModeWriteCombined)
vertices1 = UnsafeMutableRawPointer(vertexBuffer1!.contents()).bindMemory(to:GVertex.self, capacity: maxCount * maxCount * 4)
Stuff regarding images:
func mapIndicesRgba(_ imageIndices: Data, size: Size2<Int>) -> Data {
let palette = self
var pixelData = Data(count: size.area * 4)
pixelData.withUnsafeMutableBytes() { (pixels: UnsafeMutablePointer<UInt8>) in
imageIndices.withUnsafeBytes { (indices: UnsafePointer<UInt8>) in
var pixel = pixels
var raw = indices
for _ in 0..<(size.width * size.height) {
let colorIndex = raw.pointee
pixel[0] = palette[colorIndex].red
pixel[1] = palette[colorIndex].green
pixel[2] = palette[colorIndex].blue
pixel[3] = palette[colorIndex].alpha
pixel += 4
raw += 1
}
}
}
return pixelData
}
Stuff regarding input streams:
fileprivate extension InputStream {
fileprivate func loadData(sizeHint: UInt) throws -> Data {
let hint = sizeHint == 0 ? BUFFER_SIZE : Int(sizeHint)
var buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: hint)
var totalBytesRead = read(buffer, maxLength: hint)
while hasBytesAvailable {
let newSize = totalBytesRead * 3 / 2
// Ehhhh, Swift Foundation's Data doesnt have `increaseLength(by:)` method anymore
// That is why we have to go the `realloc` way... :(
buffer = unsafeBitCast(realloc(buffer, MemoryLayout<UInt8>.size * newSize), to: UnsafeMutablePointer<UInt8>.self)
totalBytesRead += read(buffer.advanced(by: totalBytesRead), maxLength: newSize - totalBytesRead)
}
if streamStatus == .error {
throw streamError!
}
// FIXME: Probably should use Data(bytesNoCopy: .. ) instead, but will it deallocate the tail of not used buffer?
// leak check must be done
let retVal = Data(bytes: buffer, count: totalBytesRead)
free(buffer)
return retVal
}
}
http://metalkit.org/2017/05/26/working-with-memory-in-metal-part-2.html
Swift semantics allows it to make copies of certain data types for safety when reading and potentially writing non-atomic-sized chunks of memory (copy-on-write allocations, etc.). This data copy operation possibly requires a memory allocation, which potentially can cause a lock with unpredictable latency.
An unsafe pointer can be used to pass a reference to a (possibly)mutable array (or block of bytes), or slice thereof, that should not be copied, no matter how (unsafely) accessed or passed around between functions or threads. This potentially reduces the need for the Swift runtime to do as many memory allocations.
I had one prototype iOS application where Swift was spending significant percentages of CPU (and likely the user’s battery life) allocating and copying multi-megabyte-sized slices of regular Swift arrays passed to functions at a very high rate, some mutating, some not mutating them (for near-real-time RF DSP analysis). A large GPU texture, sub-texture-slice accessed each frame refresh, possibly could have similar issues. Switching to unsafe pointers referencing C allocations of memory stopped this performance/battery waste in my vanilla Swift prototype (the extraneous allocate and copy operations disappeared from the performance profiling).

Directly assign UnsafePointer

Can somebody please explain to me what I'm missing here?
Given
let index: Int32 = 100
Why is this not okay:
// Use of extraneous '&'
let ptr = &index // Type inference?
Or even:
// Use of extraneous '&'
let ptr: UnsafePointer<Int32> = &index
But this is:
{
func point(num: UnsafePointer<Int32>) -> UnsafePointer<Int32> {
return num
}
let ptr = point(num: &index)
}
This would be the simple equivalent of this in C:
int index = 100;
int *ptr = &index;
Do I really have to define a function that literally takes the referenced value and passes back the very same reference? Something feels wrong about it. It seems like I'm missing something here, maybe even fundamental.
How do I assign an UnsafePointer to the memory address of the type it is (Int32 in this case)???
Thanks!
Edit:
Ultimately what I'm attempting to accomplish is, I need to write several various structures into a binary file. The variable index would be a property of a structure. The path I'm going down now involves a file OutputStream. I don't mind receiving suggestions on this, but gets out of scope of the original question.
I don't know the precise rationale, but presumably let ptr = &index isn't allowed because there's no guarantee that you can dereference ptr without invoking undefined behaviour (assuming index isn't a global or static stored variable – the only cases where Swift guarantees stable and unique pointer values).
Unlike other languages, Swift doesn't guarantee that a local variable will remain initialised until the end of the scope it's declared in – the optimiser is free to deinitialise it earlier. Allowing let ptr = &index would therefore make it far too easy to write unsound code.
It's worth noting that your example of:
func point(num: UnsafePointer<Int32>) -> UnsafePointer<Int32> {
return num
}
let ptr = point(num: &index)
is also unsound. Attempting to dereference ptr from let ptr = point(num: &index) is undefined behaviour, as the inout-to-pointer argument conversion produces a temporary pointer only valid for the duration of the function call.
If you want a scoped temporary pointer to a value, you can use withUnsafePointer(to:) – for example:
func baz() {
var foo = 5
withUnsafePointer(to: &foo) { ptr in
// use `ptr` here – do not escape it!
}
// In Swift 4.2 you can also use `withUnsafePointer(to:)` on let constants.
let bar = 5
withUnsafePointer(to: bar) { ptr in
// use `ptr` here – do not escape it!
}
}
Note that the pointer is only valid for the duration of the closure – attempting to escape it will lead to undefined behaviour.

How to convert UnsafePointer<[Float]> or fit Float Array data to Unsafe[Mutable]Pointer<Float> in Swift 3?

I have an array of float value (floatArray) coming from HDF5 file, and I'd like to use this data as an input for the function that accepts Unsafe[Mutable]Pointer (inputForFunction). I'm new to UnsafePointer, so I need someone's help.
One way I have in mind is, to store the array to a file, then to open it and to map the file descriptor to the memory of the variable for the input of the function:
// read the data from HDF5 file
var floatArray: [Float] = try originalHDF5DataSet.read()
// store the data
let dataToStore = NSData(bytes: &floatArray, length: sizeOfArray * MemoryLayout<Float>.size)
let tmpPath = URL(fileURLWithPath: NSTemporaryDirectory())
let filePath = tmpPath.appendingPathComponent("floatArrayData").appendingPathExtension("dat")
do{
try data.write(to: filePath, options: .atomic)
let fileManager : FileManager = FileManager.default
if fileManager.fileExists(atPath:filePath.path){
print("Success")
}else{
print("Fail")
}
// open the data
let dataPath = filePath.path
let fd = open(dataPath, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
assert(fd != -1, "Error: failed to open output file at \""+dataPath+"\" errno = \(errno)\n")
// memory map the parameters
let hdr = mmap(nil, Int(sizeOfArray), PROT_READ, MAP_FILE | MAP_SHARED, fd, 0)
// cast Void pointers to Float
let inputForFunction = UnsafePointer<Float>(hdr!.assumingMemoryBound(to: Float.self))
} catch{
print("failed")
}
However, it may not be the best implementation because the process to store the data seems time-consuming.
That's why I'm considering to use UnsafePointer to pass the pointer from floatArray to inputForFunction, but I'm not sure how to implement it. Just for your information, one way I'm thinking of is to use withUnsafePointermethod directly:
var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray[0]) { (ptr: UnsafePointer<Float>) in
inputForFunction = UnsafeMutablePointer(mutating: ptr)
}
or the other way is to use withMemoryRebound (but the following didn't work):
var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray) { (ptrDataArray: UnsafePointer<[Float]>) in
inputForFunction = ptrDataArray.withMemoryRebound(to: inputForFunction.self, capacity: Int(sizeOfArray), { (ptr: UnsafePointer<[Float]>) -> UnsafeMutablePointer<Float> in
return ptr[0]
})
}
My question is
How to pass the address of floatArray's data to inputForFunction?
If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?
If needed, how to use withMemoryRebound for this purpose? Can I assign
the address/value to inputForFunction inside the closure?
When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?
You can find many articles explaining how you can get Unsafe(Mutable)Pointer<Float> from [Float]. Some of them answer some of your questions. Try find them later.
How to pass the address of floatArray's data to inputForFunction?
You usually use the Array's methods withUnsafeBufferPointer or withUnsafeMutableBufferPointer. You can get an Unsafe(Mutable)BufferPointer<Element> inside the closure parameter, which contains an Unsafe(Mutable)Pointer<Element> as its baseAddress property.
var floatArray: [Float] = try originalHDF5DataSet.read()
floatArray.withUnsafeBufferPointer {unsafeBufferPointer in
let inputForFunction = unsafeBufferPointer.baseAddress
//`unsafeBufferPointer` (including its `baseAddress`) is valid only in this closure.
//So, do not pass `inputForFunction` outside of the closure, use it inside.
//...
}
If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?
When you find something like UnsafePointer<[Float]>, then you are going the wrong way. In Swift, Array is a hidden struct which may contain (multi-level) indirect reference to its elements. You usually have no need to use UnsafePointer<Array<Float>>.
If needed, how to use withMemoryRebound for this purpose? Can I assign the address/value to inputForFunction inside the closure?
When you have an Array of AType and want to pass it as an Unsafe(Mutable)Pointer<AnotherType>, you may utilize withMemoryRebound. But in your case, you only need to work with Float, so withMemoryRebound may not be needed for your purpose.
When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?
None. As I wrote above, address of floatArray is just pointing somewhere which contains a hidden struct, so it's completely useless for your purpose. And address of floatArray[0] is neither guaranteed to be the address of the first element of the whole content. Swift may create a temporal region which contains only one element, and passing the address of the temporal region.
By the way, do you really need to convert your [Float] to Unsafe(Mutable)Pointer<Float>?
If your functions' signature is something like this:
func takingUnsafePointer(_ pointer: UnsafePointer<Float>)
You can call it like:
takingUnsafePointer(floatArray)
//In this case `floatArray` can be a `let` constant.
Or else, if the function signature is:
func takingUnsafeMutablePointer(_ pointer: UnsafeMutablePointer<Float>)
Call it as:
takingUnsafeMutablePointer(&floatArray)
//In this case `floatArray` needs to be a `var`, not `let`.
Remember, in both cases, the passed pointer is valid only while the function call. You should not export the pointer somewhere else.
Array type have withUnsafeBufferPointer method.
This method accept closure with UnsafeBufferPointer which is like UnsafePointer<[Float]> you're asking for.
In you need UnsafePointer to the start of Array you can use baseAddress property of UnsafeBufferPointer.
Example code:
let bufferPointer: UnsafeBufferPointer<Float> = floatArray.withUnsafeBufferPointer { bufferPointer in
return bufferPointer
}
or
let baseAddress: UnsafePointer<Float> = floatArray.withUnsafeBufferPointer { bufferPointer in
return bufferPointer.baseAddress
}
EDIT: Or if it's function just passing &floatArray may work.

Data <-> MutableRandomAccessSlice

I am really struggling with the fact that someData[start...stop] returns a MutableRandomAccessSlice. My someData was a let to begin with, so why would I want a Mutable thing? Why don't I get just a RandomAccessSlice. What's really frustrating though, is that it returns a thing that is pretty API incompatible with the original source. With a Data, I can use .withUnsafeBytes, but not so with this offspring. And how you turn the Slice back into a Data isn't clear either. There is no init that takes one of those.
I could use the subdata(in:) method instead of subscripting, but then, what's the point of the subscript if I only ever want a sub collection representation that behaves like the original collection. Furthermore, the subdata method can only do open subranges, why the subscript can do both closed and open. Is this just something they haven't quite finished up for Swift3 final yet?
Remember that the MutableRandomAccessSlice you get back is a value type, not a reference type. It just means you can modify it if you like, but it has nothing to do with the thing you sliced it out of:
let x = Data(bytes: [1,2,3]) // <010203>
var y = x[0...1]
y[0] = 2
x // <010203>
If you look in the code, you'll note that the intent is to return a custom slice type:
public subscript(bounds: Range<Index>) -> MutableRandomAccessSlice<Data> {
get {
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)
}
}
}
That said, a custom slice will still not be acceptable to a function that takes a Data. That is consistent with other types, though, like Array, which slices to an ArraySlice which cannot be passed where an Array is expected. This is by design (and likely is for Data as well for the same reasons). The concern is that a slice "pins" all of the memory that backs it. So if you took a 3 byte slice out of a megabyte Data and stored it in an ivar, the entire megabyte has to hang around. The theory (according to Swift devs I spoke with) is that Arrays could be massive, so you need to be careful with slicing them, while Strings are usually much smaller, so it's ok for a String to slice to a String.
In my experience so far, you generally want subdata(in:). My experimentation with it is that it's very similar in speed to slicing, so I believe it's still copy on write (but it doesn't seem to pin the memory either in my initial tests). I've only tested on Mac so far, though. It's possible that there are more significant performance differences on iOS devices.
Based on Rob's comments, I just added the following pythonesque subscript extension:
extension Data {
subscript(start:Int?, stop:Int?) -> Data {
var front = 0
if let start = start {
front = start < 0 ? Swift.max(self.count + start, 0) : Swift.min(start, self.count)
}
var back = self.count
if let stop = stop {
back = stop < 0 ? Swift.max(self.count + stop, 0) : Swift.min(stop, self.count)
}
if front >= back {
return Data()
}
let range = Range(front..<back)
return self.subdata(in: range)
}
}
That way I can just do
let input = Data(bytes: [0x60, 0x0D, 0xF0, 0x0D])
input[nil, nil] // <600df00d>
input[1, 3] // <0df0>
input[-2, nil] // <f00d>
input[nil, -2] // <600d>