Heyo, ive been writing zig for like 2 years now and literally never asked what the difference between the two functions are. So far ive just been using writeall all the time because i thought it more accurately represents what I want to do: write all of the slice.
Does write just allow itself to stop writing depending on the underlying api and writeall just keeps writing until all is done or am i wrong?
ssize_t write(int fd, const void *buf, size_t count);
DESCRIPTION
write() writes up to count bytes from the buffer starting at buf to the
file referred to by the file descriptor fd.
The number of bytes written may be less than count
pub fn writeAll(self: Self, bytes: []const u8) anyerror!void {
var index: usize = 0;
while (index != bytes.len) {
index += try self.write(bytes[index..]);
}
}
writeAll repeatedly calls the write function of the Writer. This loop can execute multiple times if the Writer is buffered for example (let’s imagine, it only writes 4096 bytes at a time for example). If the write is not buffered, the loop would only execute one time, if I understand correctly.
Any library I/O function will eventually do system call (read()/recv(), write()/send() on POSIX systems). OS does not “promise” you to write all bytes at once. Instead it takes as much as it able to process at the moment and returns this number as a result of an operation. Sometimes this is called “partial write”. So if you really want to write all bytes you must do it in a loop. In event driven design I’m doing such things like this (re-enabling EPOLLOUT event if needed).
I had a chance to see the question before the post was deleted
Well, consider a TCP-socket. TCP/IP has some (finite size) buffers, NIC driver has some buffers, NIC itself has hardware buffers. Try to send, say, 1GB of data via std.posix.write(). Most likely, OS will not even attempt to accept all bytes at once, it will copy only some into kernel space buffers. So it’s up to an application to ensure that all bytes are written/sent. And, of course, all this is not about Zig (or any other lang), it’s about OS kernels design and behavior.
Same goes for “partial reads”. You won’t believe me, but beforetime I worked with some wierd “device” connected via serial port, with simple protocol (one byte out, 5 (ONLY FIVE!!!) bytes in). And sometimes, when I did read(fd,buf,5), I had those 5 bytes at once, but sometimes I had not, data arrived in two chunks (2 + 3 or so).
Haha nice I appreciate the answer! I deleted the question because after I wrote it I saw someone else already answered it, but your more in depth information is actually greatly appreciated and really interesting.
Hold on you were also the one to answer the inital question lol
This actually confirms my assumptions but its really good to know that my mental model of software is accurate!
I actually appreciate that zig exposes these “non-guarantee-fns” because if I ever wanna write something that provides a read fn I can write it much simpler and with no allocations contrary to if everything had to be readall
I just wish that this was documented in anyreader or so
RETURN VALUE
On success, the number of bytes written is returned. On error, -1 is
returned, and errno is set to indicate the cause of the error.
...
If count is zero and fd refers to a regular file, then write() may re‐
turn a failure status if one of the errors below is detected. If no
errors are detected, or error detection is not performed, 0 will be re‐
turned without causing any other effect. If count is zero and fd
refers to a file other than a regular file, the results are not speci‐
fied.
So, there is a guarantee, unless you are going to write zero bytes. But what for?
Looking at the man page mentioned, I can’t see that the guarantee is sufficient:
write() writes up to count bytes from the buffer starting at buf to the file referred to by the file de‐
scriptor fd.
The number of bytes written may be less than count if, for example, there is insufficient space on the
underlying physical medium, or the RLIMIT_FSIZE resource limit is encountered (see setrlimit(2)), or the
call was interrupted by a signal handler after having written less than count bytes. (See also pipe(7).)
The regular use-case would be to have a count > 0 as parameter. In this case the write might persistently return 0 (less than the desired count of bytes) but throw no error. This loop would never exit.
But, perhaps returning 0 when count > 0 is not allowed, but I didn’t read that in the many page.
Exactly this way. C’s write() can return 0 only if count was 0, otherwise it returns ether number of bytes written (>0) or -1. Zig’s system.write() returns -errno in case of error, if I got it right.