Unable to understand how withUnsafeBytes method works - swift

I am trying to convert Data to UnsafePointer. I found an answer here where I can use withUnsafeBytes to get the bytes.
Then I did a small test my self to see I could just print out the bytes value of the string "abc"
let testData: Data = "abc".data(using: String.Encoding.utf8)!
testData.withUnsafeBytes(
{(bytes: UnsafePointer<UInt8>) -> Void in
NSLog("\(bytes.pointee)")
})
But the output is just the value of one character, which is "a".
2018-07-11 14:40:32.910268+0800 SwiftTest[44249:651107] 97
How could I get the byte value of all three characters then?

The "pointer" points to the address of the first byte in the sequence. If you want to want a pointer to the other bytes, you have to use pointer arithmetic, that is, move the pointer to the next address:
testData.withUnsafeBytes{ (bytes: UnsafePointer<UInt8>) -> Void in
NSLog("\(bytes.pointee)")
NSLog("\(bytes.successor().pointee)")
NSLog("\(bytes.advanced(by: 2).pointee)")
}
or
testData.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
NSLog("\(bytes[0])")
NSLog("\(bytes[1])")
NSLog("\(bytes[2])")
}
However, you must be aware of the byte size of testData and don't overflow it.

You are getting '97' because 'bytes' is pointing to the staring address of the 'testdata'.
you can get byte value of all three OR n number of characters like in the following code :
let testData: Data = "abc".data(using: String.Encoding.utf8)!
print(testData.count)
testData.withUnsafeBytes(
{(bytes: UnsafePointer<UInt8>) -> Void in
for idx in 0..<testData.count {
NSLog("\(bytes[idx])")
}
})

Related

Parsing Data Out of Dictionary<String, Any> in Swift

I'm trying to extract data out of a Dictionary<String, Any> in Swift. The dictionary returns the following when I run NSLog("\(terminalDict)"):
Optional(["DFEE22": <323c3c>, "DFEE20": <3c>, "DFEE21": <0a>, "DFEE17": <07>, "DFEE1E": , "DF10": <656e6672 65737a68>, "9F1C": <38373635 34333231>, "DFEE16": <00>, "DFEE15": <01>, "5F36": <02>, "DF11": <00>, "DFEE1F": <80>, "DFEE18": <80>, "9F1A": <0840>, "9F35": <21>, "9F4E": <31303732 31205761 6c6b6572 2053742e 20437970 72657373 2c204341 202c5553 412e>, "DF27": <00>, "DFEE1B": <30303031 35313030>, "DF26": <01>, "9F15": <1234>, "9F40": <f000f0a0 01>, "9F16": <30303030 30303030 30303030 303030>, "9F33": <6028c8>, "9F1E": <5465726d 696e616c>])
I want to get all the keys and values out of the dictionary and into one string variable (newSettings). This is how I was attempting to do that:
for (key, value) in terminalDict! {
NSLog("key is now= \(key)")
NSLog("value is now= \(value)")
let asString = value as! String
print(asString)
NSLog("Adding \(key) \(asString)")
newSettings = "\(newSettings)\(key)\(asString)"
}
which returns:
key is now= DFEE22
value is now= {length = 3, bytes = 0x323c3c}
Could not cast value of type 'NSConcreteMutableData' (0x204aff148) to 'NSString' (0x204afde30).
How can I get the "323c3c" out of the dictionary as a simple string without the length and bytes portion? I can't find much documentation on type 'NSConcreteMutableData'. Do I have to use substring functions in order to get rid of the "length=x bytes=" part? I'm guessing there's a better way to do this than manually getting the substrings. Thanks.
As Vadian says, your dictionaries contain Data values.
It looks like it is ASCII encoded text, but it's a bit hard to be sure.
This bit:
31303732 31205761 6c6b6572 2053742e 20437970 72657373 2c204341 202c5553 412e
Represents the string "10721 Walker St. Cypress, CA ,USA." when you treat it as ASCII.
you could use code like this:
for (key, value) in terminalDict! {
NSLog("key is now= \(key)")
// Decode the data into a String assuming it's ASCII
let asString = String(data: value, encoding: .ascii)
NSLog("value is now= '\(asString)'")
print(asString)
NSLog("Adding \(key) \(asString)")
newSettings = "\(newSettings)\(key)\(asString)"
}

access element of fixed length C array in swift

I'm trying to convert some C code to swift.
(Why? - to use CoreMIDI in OS-X in case you asked)
The C code is like this
void printPacketInfo(const MIDIPacket* packet) {
int i;
for (i=0; i<packet->length; i++) {
printf("%d ", packet->data[i]);
}
}
And MIDIPacket is defined like this
struct MIDIPacket
{
MIDITimeStamp timeStamp;
UInt16 length;
Byte data[256];
};
My Swift is like this
func printPacketInfo(packet: UnsafeMutablePointer<MIDIPacket>){
// print some things
print("length", packet.memory.length)
print("time", packet.memory.timeStamp)
print("data[0]", packet.memory.data.1)
for i in 0 ..< packet.memory.length {
print("data", i, packet.memory.data[i])
}
}
But this gives a compiler error
error: type '(UInt8, UInt8, .. cut .. UInt8, UInt8, UInt8)'
has no subscript members
So how can I dereference the I'th element of a fixed size array?
in your case you could try to use something like this ...
// this is tuple with 8 Int values, in your case with 256 Byte (UInt8 ??) values
var t = (1,2,3,4,5,6,7,8)
t.0
t.1
// ....
t.7
func arrayFromTuple<T,R>(tuple:T) -> [R] {
let reflection = Mirror(reflecting: tuple)
var arr : [R] = []
for i in reflection.children {
// better will be to throw an Error if i.value is not R
arr.append(i.value as! R)
}
return arr
}
let arr:[Int] = arrayFromTuple(t)
print(arr) // [1, 2, 3, 4, 5, 6, 7, 8]
...
let t2 = ("alfa","beta","gama")
let arr2:[String] = arrayFromTuple(t2)
arr2[1] // "beta"
This was suggested by https://gist.github.com/jckarter/ec630221890c39e3f8b9
func printPacketInfo(packet: UnsafeMutablePointer<MIDIPacket>){
// print some things
print("length", packet.memory.length)
print("time", packet.memory.timeStamp)
let len = Int(packet.memory.length)
withUnsafePointer(&packet.memory.data) { p in
let p = UnsafeMutablePointer<UInt8>(p)
for i:Int in 0 ..< len {
print(i, p[i])
}
}
}
This is horrible - I hope the compiler turns this nonsense into some good code
The error message is a hint: it shows that MIDIPacket.data is imported not as an array, but as a tuple. (Yes, that's how all fixed length arrays import in Swift.) You seem to have noticed this in the preceding line:
print("data[0]", packet.memory.data.1)
Tuples in Swift are very static, so there isn't a way to dynamically access a tuple element. Thus, in some sense the only "safe" or idiomatic way to print your packet (in the way that you're hinting at) would be 256 lines of code (or up to 256, since the packet's length field tells you when it's safe to stop):
print("data[1]", packet.memory.data.2)
print("data[2]", packet.memory.data.3)
print("data[3]", packet.memory.data.4)
/// ...
print("data[254]", packet.memory.data.255)
print("data[255]", packet.memory.data.256)
Clearly that's not a great solution. Using reflection, per user3441734's answer, is one (cumbersome) alternative. Unsafe memory access, per your own answer (via jckarter), is another (but as the name of the API says, it's "unsafe"). And, of course, you can always work with the packet through (Obj)C.
If you need to do something beyond printing the packet, you can extend the UnsafePointer-based solution to convert it to an array like so:
extension MIDIPacket {
var dataBytes: [UInt8] {
mutating get {
return withUnsafePointer(&data) { tuplePointer in
let elementPointer = UnsafePointer<UInt8>(tuplePointer)
return (0..<Int(length)).map { elementPointer[$0] }
}
}
}
}
Notice that this uses the packet's existing length property to expose an array that has only as many valid bytes as the packet claims to have (rather than filling up the rest of a 256-element array with zeroes). This does allocate memory, however, so it might not be good for the kinds of real-time run conditions you might be using CoreMIDI in.
Should this:
for i in 0 ..< packet.memory.length
Be this?
for i in 0 ..< packet.memory.data.length

Encoding and Decoding Strings using UnsafeMutablePointer in Swift

I'm having trouble converting strings to and from UnsafeMutablePointers. The following code doesn't work, returning the wrong string.
// func rfcommChannelData(rfcommChannel: IOBluetoothRFCOMMChannel!, data dataPointer: UnsafeMutablePointer<Void>, length dataLength: Int)
func receivingData(data dataPointer: UnsafeMutablePointer<Void>, length dataLength: Int) {
let data = NSData(bytes: dataPointer, length: dataLength)
println("str = \(NSString(data: data, encoding: NSASCIIStringEncoding))")
}
// - (IOReturn)writeSync:(void *)data length:(UInt16)length;
func sendingData(data: UnsafeMutablePointer<Void>, length: UInt16) {
receivingData(data: data, length: Int(length))
}
var str: NSString = "Hello, playground"
var data = str.dataUsingEncoding(NSASCIIStringEncoding)!
var bytes = data.bytes
sendingData(&bytes, UInt16(data.length))
A link to the playground file is here. If anyone has experience using UnsafeMutablePointers in Swift for strings, I would very much appreciate some guidance as I have made no progress in the last few days. Thanks again!
With
var bytes = data.bytes
sendingData(&bytes, UInt16(data.length))
you pass the address of the variable bytes itself to the function, so what you see is the bytes that are used to represent that pointer.
What you probably want is
let str = "Hello, playground"
let data = str.dataUsingEncoding(NSASCIIStringEncoding)!
sendingData(UnsafeMutablePointer(data.bytes), UInt16(data.length))
to pass the pointer to the data bytes.
You should also consider
to use NSUTF8StringEncoding instead, because a conversion to
NSASCIIStringEncoding can fail.

Convert UInt8 Array to String

I have decrypted using AES (CrytoSwift) and am left with an UInt8 array. What's the best approach to covert the UInt8 array into an appripriate string? Casting the array only gives back a string that looks exactly like the array. (When done in Java, a new READABLE string is obtained when casting Byte array to String).
I'm not sure if this is new to Swift 2, but at least the following works for me:
let chars: [UInt8] = [ 49, 50, 51 ]
var str = String(bytes: chars, encoding: NSUTF8StringEncoding)
In addition, if the array is formatted as a C string (trailing 0), these work:
str = String.fromCString(UnsafePointer(chars)) // UTF-8 is implicit
// or:
str = String(CString: UnsafePointer(chars), encoding: NSUTF8StringEncoding)
I don't know anything about CryptoSwift. But I can read the README:
For your convenience CryptoSwift provides two function to easily convert array of bytes to NSData and other way around:
let data = NSData.withBytes([0x01,0x02,0x03])
let bytes:[UInt8] = data.arrayOfBytes()
So my guess would be: call NSData.withBytes to get an NSData. Now you can presumably call NSString(data:encoding:) to get a string.
SWIFT 3.1
Try this:
let decData = NSData(bytes: enc, length: Int(enc.count))
let base64String = decData.base64EncodedString(options: .lineLength64Characters)
This is string output
Extensions allow you to easily modify the framework to fit your needs, essentially building your own version of Swift (my favorite part, I love to customize). Try this one out, put at the end of your view controller and call in viewDidLoad():
func stringToUInt8Extension() {
var cache : [UInt8] = []
for byte : UInt8 in 97..<97+26 {
cache.append(byte)
print(byte)
}
print("The letters of the alphabet are \(String(cache))")
}
extension String {
init(_ bytes: [UInt8]) {
self.init()
for b in bytes {
self.append(UnicodeScalar(b))
}
}
}

Swift: How to convert a String to UInt8 array?

How do you convert a String to UInt8 array?
var str = "test"
var ar : [UInt8]
ar = str
Lots of different ways, depending on how you want to handle non-ASCII characters.
But the simplest code would be to use the utf8 view:
let string = "hello"
let array: [UInt8] = Array(string.utf8)
Note, this will result in multi-byte characters being represented as multiple entries in the array, i.e.:
let string = "é"
print(Array(string.utf8))
prints out [195, 169]
There’s also .nulTerminatedUTF8, which does the same thing, but then adds a nul-character to the end if your plan is to pass this somewhere as a C string (though if you’re doing that, you can probably also use .withCString or just use the implicit conversion for bridged C functions.
let str = "test"
let byteArray = [UInt8](str.utf8)
swift 4
func stringToUInt8Array(){
let str:String = "Swift 4"
let strToUInt8:[UInt8] = [UInt8](str.utf8)
print(strToUInt8)
}
I came to this question looking for how to convert to a Int8 array. This is how I'm doing it, but surely there's a less loopy way:
Method on an Extension for String
public func int8Array() -> [Int8] {
var retVal : [Int8] = []
for thing in self.utf16 {
retVal.append(Int8(thing))
}
return retVal
}
Note: storing a UTF-16 encoded character (2 bytes) in an Int8 (1 byte) will lead to information loss.