On properly aligned pointer for types Pointee and T

Little beginner's Q, please bear with me.

Compiles but run time error, and app crashes (ios 16) in latest
What changes are necessary to make it safe again?

var tH = hash.withUnsafeBytes { (ptr: UnsafePointer) -> UInt32 in

        let offset = ptr[hash.count - 1] & 0x0f
        let tPtr = ptr + Int(offset)
        return tPtr.withMemoryRebound(to: UInt32.self, capacity: 1) {
           ($0.pointee)
        }
    }

Gets this run time error : self must be a properly aligned pointer for types Pointee and T on the return statement

Many thanks in advance!

You're looking for loadUnaligned(fromByteOffset:as:). But also, if you can provide a little more context about what you're trying to accomplish, there may be a better way to do it.

1 Like

Thanks Steve.

We are trying to get 4 bytes from the hash, starting at the given byte offset as calculated and initialize tH with that value. Hash is of Data, as returned by Data(bytes: macOut, count: hashLength)

We are getting this error on ios 16 ; ios 15 works fine.

IMO it’s much better to do this stuff without using unsafe pointers. Consider:

func extractHash(hash: Data) -> UInt32? {
    guard
        let offset = hash.last.map({ Int($0 & 0x0f) }),
        (offset + 4) < hash.count
    else { return nil }
    return hash.dropFirst(offset).prefix(4).reduce(0, { $0 << 8 | UInt32($1) })
}

This assumes the UInt32 is big endian. If it’s little endian, add a .reversed(). If it’s native endian, things get a bit trickier (-:

The advantage of this approach is that it avoids unsafe constructs. You’ll never get undefined behaviour. Either it’ll work, or it’ll return nil, or I’ve forgotten a precondition and it’ll trap.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

Alternatively, create a safe version:

extension Array where Element == UInt8 {
    func loadUnaligned<T>(from offset: Index = 0, as: T.Type) -> T {
        withUnsafeBufferPointer { buffer in
          precondition(offset + MemoryLayout<T>.size <= buffer.count)
          precondition(_isPOD(T.self))
          return UnsafeRawBufferPointer(buffer).loadUnaligned(fromByteOffset: offset, as: T.self)
        }
    }
}

It is generally good to avoid unsafe APIs, but when there are significant benefits (as there can be in this case), it can be worthwhile.

Unsafe APIs do not check their preconditions at runtime in release builds -- but it is still possible to use them safely, and if the unchecked code is small and simple enough that you can reason about it and add those precondition checks yourself, you can still build safe APIs using them.

Calling a complex generic algorithm with no bounds checking is an easy path to UB, because it is difficult to reason about all of the preconditions that may be violated in all functions and called functions, and to maintain that assurance as the code evolves - but performing a single load is simple enough that we can create a safe version quite easily, in a little function that others can invoke without worrying about UB.

4 Likes

Thanks to all! I am able to fix this and the crashes :) Appreciate it very much.
best wishes for upcoming holiday season.

2 Likes

Hi tech_geek, I have same issue. How did you fix it. Thanks in advance.