Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get bytes size for arbitrary array in Swift?

I'd like to use let rawDataFromArray = NSData(bytes: myArray, length: ???), but don't know how to get the bytes length for my array. Here are some examples of what could my array be:

let arr1 = [1, 2, 3]
let arr2 = [1.0, 23556789000.0]
let arr3 = ["hello", "ok", "πŸ‘"]

func arrayLength(myArray: Array) -> Int {
    var bytes = 0
    for object in myArray {
        // not sure what to do here
    }
    return bytes
}

I'm not sure if going through every element of the array (and in case of strings going through every character, since emojis could have more bytes representing them) is the proper way to do it.

How to get bytes size for array?
Could anyone tell me the proper way to do it?
Or maybe it's just that it is not good practice to convert Array to NSData in Swift?

I've also seen Converting Swift Array to NSData for persistent storage and Converting array of bytes to NSData and Custom Array to NSData, but couldn't figure out how to get bytes size for such arbitrary array.

like image 937
Andrej Avatar asked Nov 19 '15 10:11

Andrej


People also ask

How do I get the size of an array in Swift?

To get the size of an array in Swift, use count function on the array. Following is a quick example to get the count of elements present in the array. array_name is the array variable name. count returns an integer value for the size of this array.

Is array fixed size in Swift?

Swift doesn't have fixed size arrays, and I guess many of us have experimented with various workarounds.

What is array capacity in Swift?

This allows Swift to immediately allocate an array capable of holding 512 items, as opposed to creating a small array then re-allocating multiple times.

What is byte array in Swift?

In Swift a byte is called a UInt8β€”an unsigned 8 bit integer. A byte array is a UInt8 array. In ASCII we can treat chars as UInt8 values. With the utf8 String property, we get a UTF8View collection. We can convert this to a byte array.


1 Answers

There seems to be a misunderstanding: For each type T, all instances of T have the same size which can be computed as sizeof(T). In the case of arrays, there can be a padding between array elements, therefore the total size needed for arr1 is

arr1.count * strideof(Int)

(Compare e.g. Swift: How to use sizeof? for the subtle differences between sizeof() and strideof()).

Therefore a generic function to create NSData from an array would be

extension Array {
    func asData() -> NSData {
        return self.withUnsafeBufferPointer({
            NSData(bytes: $0.baseAddress, length: count * strideof(Element))
        })
    }
}

Using withUnsafeBufferPointer() guarantees that the array uses contiguous storage for its elements.

In the case of "simple" types like Int and Float this gives the expected results:

let arr1 = [1, 2, 3]
print(arr1.asData())
// <01000000 00000000 02000000 00000000 03000000 00000000>

let arr2 = [1.0, 23556789000.0]
print(arr2.asData())
// <00000000 0000f03f 0000204c 60f01542>

However, it is useless for an array of strings:

let arr3 = ["hello", "ok", "πŸ‘"]
print(arr3.asData())
// <945b2900 01000000 05000000 00000000 00000000 00000000 9a5b2900 01000000 02000000 00000000 00000000 00000000 068d2900 01000000 02000000 00000080 00000000 00000000>

because struct String contains (hidden/undocumented) pointers to the actual character storage.

One possibility would be to append each string as a NUL-terminated UTF-8 string:

let data3 = NSMutableData()
arr3.forEach { string in
    string.withCString {
        data3.appendBytes($0, length: Int(strlen($0)) + 1)
    }
}
print(data3)
// <68656c6c 6f006f6b 00f09f91 8d00>

Alternatively, use NSKeyedArchiver as in the threads that you referenced.

like image 133
Martin R Avatar answered Sep 20 '22 18:09

Martin R