withUnsafeBytes' is deprecated

Xcode tells me:
'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead

But I cannot figure out, how to use this correctly.

Any Help?

Current code:
let bigNum: UInt64 = data.withUnsafeBytes
{ (pointer: UnsafePointer) -> UInt64 in
return pointer.pointee
}
Works, but creates warning.

Gerriet.

It looks like you're not the only one who's run into issues with UnsafeRawBufferPointer (h/t @mjtsai). I'm not terribly familiar with the API so I don't know how safe the following code is, but at least it compiles:

let bigNum: UInt64 = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> UInt64 in
    return bytes.load(as: UInt64.self)
}

You could write this more concisely as follows:

let bigNum: UInt64 = data.withUnsafeBytes { bytes in
    return bytes.load(as: UInt64.self)
}

Or you could even make it a one-liner:

let bigNum = data.withUnsafeBytes { $0.load(as: UInt64.self) }
2 Likes

Thanks a lot! Works perfectly.

Gerriet.

let bigNum = data.withUnsafeBytes { $0.load(as: UInt64.self) }

That's safe if data is 8-byte aligned. And it's currently the best way to do it IMO. In the past I've suggested adding Data APIs to bypass the unsafe closure-based API, like this, but we're not there yet:

e.g. let bigNum = data.load(as: UInt64.self) }

We also don't have an API for loading unaligned data, so if alignment can't be guaranteed you still have to do something like this:

  var bigNum = Int64(0)
  withUnsafeMutableBytes(of: &bigNum) {
    data.copyBytes(to: $0, from: 0..<MemoryLayout<Int64>.size)
  }

...and it just took me a long time to figure out the right incantation for that.

4 Likes

Thanks for clearing this up!

One additional question: would
let subdata = data.subdata(in: 3 ..< 3 + 8)
be 8-byte aligned?

But I find this all very confusing.

I really would like to write:
let bigNum = UInt64(data: data)
or:
let bigNum = UInt64(data: data, offset: 7)

and would expect nil (if data is too short) or a value.

Even better (as I am receiving data in network byte order) would be:
let bigNum = UInt64(data: data, endianness: .bigEndian) // converts from bigEndian to host endianness

I am sure the compiler could figure this out.

Gerriet.

Unless I am mistaken, this can be shortened to

var bigNum = Int64(0)
withUnsafeMutableBytes(of: &bigNum) {
  data.copyBytes(to: $0)
}

since copyBytes() limits the number of bytes to copy to the size of the target buffer.

2 Likes
  var bigNum = Int64(0)
  withUnsafeMutableBytes(of: &bigNum) {
    data.copyBytes(to: $0)
  }

Thanks @Martin. I just realized that API was added to DataProtocol where I forgot to look. Now I can close this bug! [SR-9720] Foundation Data API: add Data.copyBytes methods taking UnsafeRawBufferPointer. · Issue #3553 · apple/swift-corelibs-foundation · GitHub

I filed a new JIRA for this, [SR-10778] Foundation Data should have an API for reading/writing arbitrary trivial types (without exposing unsafe pointers) · Issue #3329 · apple/swift-corelibs-foundation · GitHub, based on an old radar rdar://problem/28201395 Add Foundation Data API for moving typed values into and out of raw bytes. /cc @Philippe_Hausler, @itaiferber I would be nice to have this soon so people aren't always forced to understand how to work with unsafe pointers.

No, unless data happened to be misaligned to begin with.

I really would like to write:
let bigNum = UInt64(data: data)
or:
let bigNum = UInt64(data: data, offset: 7)

and would expect nil (if data is too short) or a value.

I suppose extensions could be provided for the scalar types. I don't think it can replace a generic copyBytes API. Note that Data APIs use indices instead of byte offsets. I also think it's more conventional for Swift libraries to trap on something the user would normally check for themselves rather than force the user to unwrap an optional.

Even better (as I am receiving data in network byte order) would be:
let bigNum = UInt64(data: data, endianness: .bigEndian) // converts from bigEndian to host endianness

I think the conventional way to do it is
UInt64(data: data).bigEndian

Actually, I agree with your original suggestion. I added a note about this to the JIRA bug.

I have the same warning in my function and really don't know how to fix it. Anyone have any idea ?

func writeData(_ data: Data) {
    
    _ = data.withUnsafeBytes { // warning here
        self.write($0, maxLength: data.count)
    }
}

The use of write(_:maxLength:) makes me suspect you have an extension on OutputStream. If so, see this DevForums thread.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

This change fixed my warning:

func writeData(_ data: Data) {
        
        _ = data.withUnsafeBytes {
            write($0.bindMemory(to: UInt8.self).baseAddress!, maxLength: data.count)
        }
    }