Trying to read from stdin using `read`, failing at it because of weird pointer movements

I have the following code:

#!/usr/bin/swift

import Foundation

for _ in 0..<20 {
    var buffer = UnsafeMutableRawPointer.allocate(byteCount: 1_024, alignment: 1)
    let bufferState = buffer
    print("init address", buffer)
    let readCount = read(0, &buffer, 1_024)
    print("after read address", buffer)
    print("read count", readCount)

    print("difference in addresses", buffer - bufferState)
}

what I want to do is to be able to read stdin (fd 0) using read, since this is part of a larger block using kqueues. This piece of code fails to do this for me though, and I can't undersand why. I'd expect that buffer stays in the same address before and after read is invoked, but that is not the case, as it jumps to random addresses.

Here's some sample output on Big Sur 11.4, compiled with Xcode 12.5.1:

init address 0x000000014cd6de00
ahjsd
after read address 0x00000a64736a6861
read count 6
difference in addresses 11420965243489
init address 0x000000013f008200
hjskdf
after read address 0x000a66646b736a68
read count 7
difference in addresses 2927325900630120

Notice that the readCount is correct, it reports back the right number of read bytes. But I'd expect the difference to be effectively 0.

I've also tried reading from bufferState instead, but it just contains garbage. The new buffer address also points to garbage, so I don't know where the input is going.

Is this code correct, or are the movements expected? What am I missing?

Remove & from read(..., &buffer, ...).
You have too many levels of indirections.

PS

Don't forget to deallocate buffer if you haven't done so in your non-toy code. This example is leaking memory.

This one is actually quite simple. You're not passing the buffer pointer to read, your passing the address of the buffer pointer to read. The code you want is this:

let readCount = read(0, buffer, 1_024)

:man_facepalming:

that's what i get for staying late working on this... thanks everyone

yup, took it out here because it was crashing on account of freeing random addresses

Two things:

  • Assuming it meets your deployment target requirement, Swift System is your friend!

  • You can reduce your reliance on unsafe pointers by using an array.

So, combining these:

import System

let fd: FileDescriptor = …

var buffer = [UInt8](repeating: 0, count: 1024)
let bytesRead = try buffer.withUnsafeMutableBytes { buf in
    try fd.read(into: buf)
}

Or, if you can’t use System:

let fd: CInt = …
var buffer = [UInt8](repeating: 0, count: 1024)
let bytesRead = read(fd, &buffer, buffer.count)

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

7 Likes