Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ UTARGETS += test_batchsvd
MODULES_test_batchsvd = -llowrank

# lib misc
UTARGETS += test_pattern test_types test_misc test_memcfl test_tree test_streams
UTARGETS += test_pattern test_types test_misc test_memcfl test_tree test_streams test_io

# lib moba
UTARGETS += test_moba
Expand Down
54 changes: 45 additions & 9 deletions src/win/vdprintf.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,57 @@
#ifndef _VDPRINTF_WINDOWS
#define _VDPRINTF_WINDOWS

#include <stdarg.h>
#include <stdio.h>
#include "misc/misc.h"
#include <stdlib.h>
#include <io.h>

int vdprintf(int, const char*, va_list);

// Formats into memory and writes the bytes to the descriptor, as POSIX
// vdprintf does. A stdio stream opened over the descriptor for each call
// could not be closed without closing the descriptor, and the C runtime
// holds a fixed number of streams per process.
int vdprintf(int fd, const char *format, va_list ap)
{
FILE* stream = _fdopen(fd, "a");
if (stream == NULL)
error("Unable to open file.\n");
int err = vfprintf(stream, format, ap);
if (err == -1)
error("Unable to write to file.\n");
fflush(stream);
return err;
char small[256];
char* buf = small;

va_list aq;
va_copy(aq, ap);
int len = vsnprintf(small, sizeof small, format, aq);
va_end(aq);

if (len < 0)
return -1;

if ((size_t)len >= sizeof small) {

if (NULL == (buf = malloc((size_t)len + 1)))
return -1;

vsnprintf(buf, (size_t)len + 1, format, ap);
}

int done = 0;

while (done < len) {

int n = _write(fd, buf + done, (unsigned int)(len - done));

if (n <= 0) {

done = -1;
break;
}

done += n;
}

if (small != buf)
free(buf);

return done;
}

#endif /* _VDPRINTF_WINDOWS */
71 changes: 71 additions & 0 deletions utests/test_io.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* Copyright 2026. Pulserver contributors.
* All rights reserved. Use of this source code is governed by
* a BSD-style license which can be found in the LICENSE file.
*
* Headers written through the descriptor-level printf, many of them in one
* process, as a program that runs BART's commands in-process writes them.
*/

#include <stdbool.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

#include "misc/misc.h"
#include "misc/io.h"

#ifdef _WIN32
#include "win/open_patch.h"
#endif

#include "utest.h"


static bool test_cfl_header_length_is_its_bytes(void)
{
const char* name = "test_io_header.hdr";
bart_dim_t dims[16] = { 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5 };

int fd = open(name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);

UT_RETURN_ON_FAILURE(-1 != fd);

int written = write_cfl_header(fd, NULL, 16, dims);

off_t size = lseek(fd, 0, SEEK_END);

close(fd);
unlink(name);

UT_RETURN_ASSERT((0 < written) && (size == written));
}

UT_REGISTER_TEST(test_cfl_header_length_is_its_bytes);


static bool test_many_cfl_headers_in_one_process(void)
{
#ifdef _WIN32
const char* null_file = "nul";
#else
const char* null_file = "/dev/null";
#endif
bart_dim_t dims[16] = { 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5 };

int fd = open(null_file, O_WRONLY);

UT_RETURN_ON_FAILURE(-1 != fd);

// Each header is a few dozen formatted writes, so this is more writes
// than a C runtime holds streams.
bool ok = true;

for (int i = 0; ok && (i < 200); i++)
ok = (0 < write_cfl_header(fd, NULL, 16, dims));

close(fd);

UT_RETURN_ASSERT(ok);
}

UT_REGISTER_TEST(test_many_cfl_headers_in_one_process);
Loading