Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - converting from UnsafePointer<UInt8> with length to String

I considered a lot of similar questions, but still can't get the compiler to accept this.

Socket Mobile API (in Objective-C) passes ISktScanDecodedData into a delegate method in Swift (the data may be binary, which I suppose is why it's not provided as string):

func onDecodedData(device: DeviceInfo?, DecodedData d: ISktScanDecodedData?) {
  let symbology: String = d!.Name()
  let rawData: UnsafePointer<UInt8> = d!.getData()
  let rawDataSize: UInt32 = decoded!.getDataSize()
  // want a String (UTF8 is OK) or Swifty byte array...
}

In C#, this code converts the raw data into a string:

string s = Marshal.PtrToStringAuto(d.GetData(), d.GetDataSize());

In Swift, I can get as far as UnsafeArray, but then I'm stuck:

let rawArray = UnsafeArray<UInt8>(start: rawData, length: Int(rawDataSize))

Alternatively I see String.fromCString and NSString.stringWithCharacters, but neither will accept the types of arguments at hand. If I could convert from UnsafePointer<UInt8> to UnsafePointer<()>, for example, then this would be available (though I'm not sure if it would even be safe):

NSData(bytesNoCopy: UnsafePointer<()>, length: Int, freeWhenDone: Bool)

Is there an obvious way to get a string out of all this?

like image 386
macu Avatar asked Jul 30 '14 16:07

macu


2 Answers

This should work:

let data = NSData(bytes: rawData, length: Int(rawDataSize))
let str = String(data: data, encoding: NSUTF8StringEncoding)

Update for Swift 3:

let data = Data(bytes: rawData, count: Int(rawDataSize))
let str = String(data: data, encoding: String.Encoding.utf8)

The resulting string is nil if the data does not represent a valid UTF-8 sequence.

like image 104
Martin R Avatar answered Nov 17 '22 12:11

Martin R


How about this, 'pure' Swift 2.2 instead of using NSData:

public extension String {

  static func fromCString
    (cs: UnsafePointer<CChar>, length: Int!) -> String?
  {
    if length == .None { // no length given, use \0 standard variant
      return String.fromCString(cs)
    }

    let buflen = length + 1
    var buf    = UnsafeMutablePointer<CChar>.alloc(buflen)
    memcpy(buf, cs, length))
    buf[length] = 0 // zero terminate
    let s = String.fromCString(buf)
    buf.dealloc(buflen)
    return s
  }
}

and Swift 3:

public extension String {

  static func fromCString
    (cs: UnsafePointer<CChar>, length: Int!) -> String?
  {
    if length == nil { // no length given, use \0 standard variant
      return String(cString: cs)
    }

    let buflen = length + 1
    let buf    = UnsafeMutablePointer<CChar>.allocate(capacity: buflen)
    memcpy(buf, cs, length)
    buf[length] = 0 // zero terminate
    let s = String(cString: buf)
    buf.deallocate(capacity: buflen)
    return s
  }
}

Admittedly it's a bit stupid to alloc a buffer and copy the data just to add the zero terminator.

Obviously, as mentioned by Zaph, you need to make sure your assumptions about the string encoding are going to be right.

like image 27
hnh Avatar answered Nov 17 '22 13:11

hnh