summaryrefslogtreecommitdiff
path: root/lib/libc/stdio/fwrite_unlocked.c
diff options
context:
space:
mode:
authorKacper <kacper@mail.openlinux.dev>2025-12-22 23:27:56 +0100
committerKacper <kacper@mail.openlinux.dev>2025-12-22 23:30:32 +0100
commit0f30d227497418c6d3bef7d52244407e30454504 (patch)
tree0e1ac19623d3268380cf74328cdf643648a2f43c /lib/libc/stdio/fwrite_unlocked.c
parent90dad97fc07f049383903a166631e2c257f9b8c1 (diff)
Added c11 threads, fixed some locks and add *_unlocked functions
Diffstat (limited to 'lib/libc/stdio/fwrite_unlocked.c')
-rw-r--r--lib/libc/stdio/fwrite_unlocked.c106
1 files changed, 106 insertions, 0 deletions
diff --git a/lib/libc/stdio/fwrite_unlocked.c b/lib/libc/stdio/fwrite_unlocked.c
new file mode 100644
index 00000000..dd5f4c0b
--- /dev/null
+++ b/lib/libc/stdio/fwrite_unlocked.c
@@ -0,0 +1,106 @@
+#include "__stdio.h"
+#include <atomic.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+extern void __libc_init_io(void);
+void *__libc_force_io_init = (void *)__libc_init_io;
+
+size_t fwrite_unlocked(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream)
+{
+ size_t total_bytes;
+ size_t written = 0;
+ const char *data = (const char *)ptr;
+
+ if (ptr == NULL || stream == NULL || size == 0) {
+ if (size == 0) {
+ return nitems;
+ }
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (__builtin_mul_overflow(size, nitems, &total_bytes)) {
+ errno = EOVERFLOW;
+ return 0;
+ }
+
+ if ((stream->flags & O_ACCMODE) == O_RDONLY) {
+ errno = EBADF;
+ return 0;
+ }
+
+ if (stream->flags & _IO_ERR) {
+ errno = EIO;
+ return 0;
+ }
+
+ /* Handle in-memory buffer case (e.g., string streams) */
+ if (stream->fd == -1 && stream->buf != NULL) {
+ size_t space_left = (stream->buf_size > stream->buf_len) ?
+ stream->buf_size - stream->buf_len - 1 /* Reserve for '\0' */
+ :
+ 0;
+ if (space_left == 0) {
+ return 0;
+ }
+ size_t to_copy = (total_bytes < space_left) ? total_bytes : space_left;
+ if (to_copy > 0) {
+ memcpy(stream->buf + stream->buf_len, data, to_copy);
+ stream->buf_len += to_copy;
+ }
+ return (to_copy == total_bytes) ? nitems : to_copy / size;
+ }
+
+ if (stream->type != _IONBF && stream->buf == NULL) {
+ errno = EIO;
+ return 0;
+ }
+
+ /* Unbuffered mode: direct write */
+ if (stream->type == _IONBF) {
+ ssize_t result = write(stream->fd, data, total_bytes);
+ if (result < 0) {
+ stream->flags |= _IO_ERR;
+ return 0;
+ }
+ return (result == (ssize_t)total_bytes) ? nitems : result / size;
+ }
+
+ size_t remaining = total_bytes;
+ while (remaining > 0) {
+ size_t space_available = stream->buf_size - stream->buf_len;
+ if (space_available == 0) {
+ if (fflush_unlocked(stream) != 0) {
+ return written / size;
+ }
+ space_available = stream->buf_size;
+ }
+ size_t to_copy = (remaining < space_available) ? remaining : space_available;
+ memcpy(stream->buf + stream->buf_len, data + written, to_copy);
+ stream->buf_len += to_copy;
+ written += to_copy;
+ remaining -= to_copy;
+
+ /* Line-buffered: flush on newline in the copied chunk */
+ if (stream->type == _IOLBF) {
+ char *newline_pos = memchr(stream->buf + stream->buf_len - to_copy, '\n', to_copy);
+ if (newline_pos != NULL) {
+ if (fflush_unlocked(stream) != 0) {
+ return written / size;
+ }
+ }
+ } else if (stream->type == _IOFBF && stream->buf_len == stream->buf_size) {
+ /* Full-buffered: flush when buffer is full */
+ if (fflush_unlocked(stream) != 0) {
+ return written / size;
+ }
+ }
+ }
+
+ return (written == total_bytes) ? nitems : written / size;
+}