Appending to Data using withUnsafeBytes appends wrong number of bytes?

I have a swift structure which contains a number of items:

struct ConfigParams {

	var MessageID: UInt16 = 0
	var SN: UInt16 = 1
	var ManufactureData: UInt32 = 0
	var Version = [Character](repeating:"?", count:16)
}

And a member function that returns this as a Data type for transmission over the netork:

func asData() -> Data {
		var data = Data()
		
		data.append(withUnsafeBytes(of: MessageID) { Data($0)})
		data.append(withUnsafeBytes(of: SN) { Data($0)})
		data.append(withUnsafeBytes(of: ManufactureData) { Data($0)})
		
		print("My size before \(data.count)")
		data.append(withUnsafeBytes(of: Version) { Data($0)})
		print("My size after \(data.count)")
		print(Version.count)

    return data
}

When I call "asData()" it gives a Data() with the wrong count of bytes in it! The output of calling the above method is:

My size before 8

My size after 16

16

So when I append the contents of Version it it only adding 8 bytes, not 16. But I would expect the final size to be 24! What am I missing here?

Answered by DTS Engineer in 741241022

There are actually two things wrong here.

  1. The global withUnsafeBytes(of:) function is only useful when the raw bytes are stored as the direct value itself, such as with "trivial" types like UInt16. An array isn't a trivial type because it doesn't store its data inline in that way.

  2. The Character type isn't a trivial type either. It has no pre-determined representation in a known number of bits.

Since your Version seems to be intended to be an array of C-style characters (i.e. as 8-bit bytes), you'd probably be better off modeling it like this:

    var Version = [UInt8](repeating:0x3F, count:16)

Now, since that array has a trivial element type, you can use Array's withUnsafeBytes(_:) method to get scoped access to a contiguous buffer of the 16 raw bytes:

        Version.withUnsafeBufferPointer { bytes in
            data.append(contentsOf: bytes)
        }

Of course, this has other code consequences, because it changes the element type of the Version array.

Accepted Answer

There are actually two things wrong here.

  1. The global withUnsafeBytes(of:) function is only useful when the raw bytes are stored as the direct value itself, such as with "trivial" types like UInt16. An array isn't a trivial type because it doesn't store its data inline in that way.

  2. The Character type isn't a trivial type either. It has no pre-determined representation in a known number of bits.

Since your Version seems to be intended to be an array of C-style characters (i.e. as 8-bit bytes), you'd probably be better off modeling it like this:

    var Version = [UInt8](repeating:0x3F, count:16)

Now, since that array has a trivial element type, you can use Array's withUnsafeBytes(_:) method to get scoped access to a contiguous buffer of the 16 raw bytes:

        Version.withUnsafeBufferPointer { bytes in
            data.append(contentsOf: bytes)
        }

Of course, this has other code consequences, because it changes the element type of the Version array.

Perfect! Thanks :-)

Appending to Data using withUnsafeBytes appends wrong number of bytes?
 
 
Q