Unsafe Swift: Using Pointers and Interacting With C

In this tutorial, you’ll learn how to use unsafe Swift to directly access memory through a variety of pointer types. By Brody Eller.

4.7 (18) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Unsafe Swift Example 2: Random Generator

Random numbers are important for many applications, from games to machine learning.

macOS provides arc4random, that produces cryptographically-sound random numbers. Unfortunately, this call is not available on Linux. Moreover, arc4random only provides randoms as UInt32. However, /dev/urandom provides an unlimited source of good random numbers.

In this section, you’ll use your new knowledge to read this file and create type-safe random numbers.

hexdump

Start by creating a new playground, calling it RandomNumbers, or by opening the playground in the begin project.

Make sure to select the macOS platform this time.

Once ready, replace the default contents with:

import Foundation

enum RandomSource {
  static let file = fopen("/dev/urandom", "r")!
  static let queue = DispatchQueue(label: "random")
  
  static func get(count: Int) -> [Int8] {
    let capacity = count + 1 // fgets adds null termination
    var data = UnsafeMutablePointer<Int8>.allocate(capacity: capacity)
    defer {
      data.deallocate()
    }
    queue.sync {
      fgets(data, Int32(capacity), file)
    }
    return Array(UnsafeMutableBufferPointer(start: data, count: count))
  }
}

You declare the file variable static so only one will exist in the system. You’ll rely on the system closing it when the process exits.

Since multiple threads may want random numbers, you need to protect access to it with a serial GCD queue.

The get function is where the work happens.

First, create unallocated storage that is one beyond what you need because fgets is always 0 terminated.

Next, get the data from the file, making sure to do so while operating on the GCD queue.

Finally, copy the data to a standard array by first wrapping it in a UnsafeMutableBufferPointer that can act as a Sequence.

So far, this will only safely give you an array of Int8 values. Now you’re going to extend that.

Add the following to the end of your playground:

extension BinaryInteger {
  static var randomized: Self {
    let numbers = RandomSource.get(count: MemoryLayout<Self>.size)
    return numbers.withUnsafeBufferPointer { bufferPointer in
      return bufferPointer.baseAddress!.withMemoryRebound(
        to: Self.self,
        capacity: 1) {
        return $0.pointee
      }
    }
  }
}

Int8.randomized
UInt8.randomized
Int16.randomized
UInt16.randomized
Int16.randomized
UInt32.randomized
Int64.randomized
UInt64.randomized

This adds a static randomized property to all subtypes of the BinaryInteger protocol. For more on this, check out our tutorial on protocol-oriented programming.

First, you get the random numbers. With the bytes of the array that get returned, you then rebind the Int8 values as the type requested and return a copy.

And that’s it! You’re now generating random numbers in a safe way, using unsafe Swift under the hood.

Where to Go From Here?

Congratulations on finishing this tutorial! You can download the completed project files at the top or bottom of this tutorial using the Download Materials.

There are many additional resources you can explore to learn more about using unsafe Swift:

I hope you’ve enjoyed this tutorial. If you have questions or experiences you would like to share, feel free to share them in the forums!