Chen-Tsu Lin
Chen-Tsu Lin

Reputation: 23224

How to convert String to UnsafePointer<UInt8> and length

When I using NSOutputStream's write method

func write(_ buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int

I don't know how to convert String to UnsafePointer<UInt8> and length

How can I do this in swift?

Upvotes: 32

Views: 30731

Answers (10)

Nathan F.
Nathan F.

Reputation: 3469

I've created a Swift extension that includes this feature among many others for generating pointers from Strings.

It includes a full test suite and it supports:

  • myString.stackPointer() -> UnsafePointer<Int8>?
  • myString.mutableStackPointer() -> UnsafeMutablePointer<Int8>?
  • myString.withUnsignedStackPointer { (ptr: UnsafePointer<UInt8>?) in
  • myString.withUnsignedMutableStackPointer { (ptr: UnsafeMutablePointer<UInt8>?) in
  • myString.heapPointer() -> UnsafePointer<Int8>?
  • myString.mutableHeapPointer() -> UnsafeMutablePointer<Int8>?
  • myString.unsignedHeapPointer() -> UnsafePointer<UInt8>?
  • myString.unsignedMutableHeapPointer() -> UnsafeMutablePointer<UInt8>?

https://gist.github.com/nathan-fiscaletti/892e074dc14e6707603414cd2d80c287

If you want to test it out you should be able to directly paste it into a Swift Playground.

Upvotes: 1

funct7
funct7

Reputation: 3591

By far the easiest way (in Swift 5):

let s = "hello, world"
let pointer = UnsafePointer(Array(s.utf8CString))

Not sure how backwards compatible this is.

Upvotes: 4

Jirui
Jirui

Reputation: 189

Swift 4,

Convert String to NSString, then use NSString's methods.

let text = "Hello"
let pointer: UnsafePointer<Int8>? = NSString(string: text).utf8String
let length = NSString(string: text).length

Upvotes: 9

BJ Miller
BJ Miller

Reputation: 1548

I see there are other answers, and an accepted answer, so it seems you've gotten what you need. I came here because I noticed Swift 5's deprecation warnings for withUnsafeMutableBytes et al, and started testing @abdullahselek's answer, but I noticed in Swift 5 (haven't yet verified if it worked in previous versions) that String is convertible to UnsafePointer<UInt8> in-line, so you can use it in place where an UnsafePointer<UInt8> is expected. In case it helps to see another example, here's our old and updated function, posted here:

OLD

let derivationStatus = localDerivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in
  salt.withUnsafeBytes { saltBytes in

    CCKeyDerivationPBKDF(
      CCPBKDFAlgorithm(kCCPBKDF2),
      password,
      passwordData.count,
      saltBytes,
      salt.count,
      algorithm,
      UInt32(rounds),
      derivedKeyBytes,
      derivedKeyData.count
    )
  }
}

NEW

let derivationStatus = localDerivedKeyData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) -> Int32 in
  let status = CCKeyDerivationPBKDF(
    CCPBKDFAlgorithm(kCCPBKDF2),
    password, // a String
    passwordData.count, // just the password String converted to Data
    String(data: salt, encoding: .utf8),  // converts salt (Data) to String
    salt.count,
    algorithm,
    UInt32(rounds),
    outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
    derivedKeyData.count
  )
  return status
}

With that said, you could use a similar approach to get your stream as follows:

let stream = OutputStream(toBuffer: UnsafeMutablePointer(mutating: someString), capacity: someString.data(using: .utf8)!.count)

(the ! is used to silence the compiler error, but you should avoid force-unwrapping where possible).

Upvotes: 1

abdullahselek
abdullahselek

Reputation: 8435

Here is a string extension for Swift 5 that you can convert a string to UnsafePointer<UInt8> and UnsafeMutablePointer<Int8>

extension String {
    func toUnsafePointer() -> UnsafePointer<UInt8>? {
        guard let data = self.data(using: .utf8) else {
            return nil
        }

        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
        let stream = OutputStream(toBuffer: buffer, capacity: data.count)
        stream.open()
        let value = data.withUnsafeBytes {
            $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
        }
        guard let val = value else {
            return nil
        }
        stream.write(val, maxLength: data.count)
        stream.close()

        return UnsafePointer<UInt8>(buffer)
    }

    func toUnsafeMutablePointer() -> UnsafeMutablePointer<Int8>? {
        return strdup(self)
    }
}

To convert UnsafeMutablePointer<Int8> to String

guard let mutablePointer = "test".toUnsafeMutablePointer() else {
    return
}

let str = String(cString: mutablePointer)

Upvotes: 5

onmyway133
onmyway133

Reputation: 48075

Here is how to do it in Swift 3. Run fine in Swift 4 too

extension String {

  func toPointer() -> UnsafePointer<UInt8>? {
    guard let data = self.data(using: String.Encoding.utf8) else { return nil }

    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
    let stream = OutputStream(toBuffer: buffer, capacity: data.count)

    stream.open()
    data.withUnsafeBytes({ (p: UnsafePointer<UInt8>) -> Void in
      stream.write(p, maxLength: data.count)
    })

    stream.close()

    return UnsafePointer<UInt8>(buffer)
  }
}

To convert from String to UnsafeMutablePointer<Int8>

let cString = strdup("Hello") // UnsafeMutablePointer<Int8>

To convert from UnsafeMutablePointer<Int8> to String

let string = String(cString: cString!) // String

Upvotes: 17

David Hertel
David Hertel

Reputation: 61

An answer for people working in Swift 4 now. You can no longer get bytes from a Data object, you have to copy them into an UnsafeMutablePointer

let helloWorld = "Hello World!"

let data = helloWorld.data(using: String.Encoding.utf8, allowLossyConversion: false)!
var dataMutablePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)

//Copies the bytes to the Mutable Pointer
data.copyBytes(to: dataMutablePointer, count: data.count)

//Cast to regular UnsafePointer
let dataPointer = UnsafePointer<UInt8>(dataMutablePointer)

//Your stream
oStream.write(dataPointer, maxLength: data.count)

Upvotes: 6

kintan
kintan

Reputation: 19

file.cString(using: String.Encoding.utf8)

Upvotes: -1

Martin R
Martin R

Reputation: 539685

You have to convert the string to UTF-8 data first

let string = "foo bar"
let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!

and then write it to the output stream

let outputStream: NSOutputStream = ... // the stream that you want to write to
let bytesWritten = outputStream.write(UnsafePointer(data.bytes), maxLength: data.length)

The UnsafePointer() cast is necessary because data.bytes has the type UnsafePointer<Void>, and not UnsafePointer<UInt8> as expected by the write() method.


Update for Swift 3:

let string = "foo bar"
// Conversion to UTF-8 data (cannot fail):
let data = string.data(using: String.Encoding.utf8)! 

// Write to output stream:
let outputStream: NSOutputStream = ... // the stream that you want to write to
let bytesWritten = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) }

Upvotes: 24

Lou Zell
Lou Zell

Reputation: 5597

You can also let Swift do it for you!

import Foundation

// Example function:
func printUTF8Vals(_ ptr: UnsafePointer<UInt8>, _ len: Int) { 
    for i in 0..<len { 
        print(ptr[i]) 
    } 
} 

// Call it:
let str = "Hello"
printUTF8Vals(str, str.lengthOfBytes(using: String.Encoding.utf8))

// Prints:
// 72
// 101
// 108
// 108
// 111

Upvotes: 6

Related Questions