Skip to content

Commit

Permalink
#127: bin/base64: fix compilation on MinGW
Browse files Browse the repository at this point in the history
As of v0.5.1, the `base64' demo program no longer compiled under MinGW
because MinGW does not emulate the `writev(2)' system call.

Fix this by detecting MinGW at compile time and optionally emulating
`writev(2)' as a series of `write(2)' calls.

Resolves #127.
  • Loading branch information
aklomp committed Jan 9, 2024
1 parent ce31662 commit fff4fb8
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 8 deletions.
19 changes: 14 additions & 5 deletions Makefile
@@ -1,4 +1,4 @@
CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic
CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic -DBASE64_STATIC_DEFINE

# Set OBJCOPY if not defined by environment:
OBJCOPY ?= objcopy
Expand Down Expand Up @@ -56,6 +56,7 @@ ifdef OPENMP
CFLAGS += -fopenmp
endif

TARGET := $(shell $(CC) -dumpmachine)

.PHONY: all analyze clean

Expand All @@ -64,9 +65,17 @@ all: bin/base64 lib/libbase64.o
bin/base64: bin/base64.o lib/libbase64.o
$(CC) $(CFLAGS) -o $@ $^

lib/libbase64.o: $(OBJS)
$(LD) -r -o $@ $^
$(OBJCOPY) --keep-global-symbols=lib/exports.txt $@
# Workaround: mangle exported function names on MinGW32.
lib/exports.build.txt: lib/exports.txt
ifeq (i686-w64-mingw32, $(TARGET))
sed -e 's/^/_/' $< > $@
else
cp -f $< $@
endif

lib/libbase64.o: lib/exports.build.txt $(OBJS)
$(LD) -r -o $@ $(OBJS)
$(OBJCOPY) --keep-global-symbols=$< $@

lib/config.h:
@echo "#define HAVE_AVX512 $(HAVE_AVX512)" > $@
Expand Down Expand Up @@ -97,4 +106,4 @@ analyze: clean
scan-build --use-analyzer=`which clang` --status-bugs make

clean:
rm -f bin/base64 bin/base64.o lib/libbase64.o lib/config.h $(OBJS)
rm -f bin/base64 bin/base64.o lib/libbase64.o lib/config.h lib/exports.build.txt $(OBJS)
72 changes: 70 additions & 2 deletions bin/base64.c
@@ -1,4 +1,19 @@
#define _XOPEN_SOURCE // IOV_MAX
// Test for MinGW.
#if defined(__MINGW32__) || defined(__MINGW64__)
# define MINGW
#endif

// Decide if the writev(2) system call needs to be emulated as a series of
// write(2) calls. At least MinGW does not support writev(2).
#ifdef MINGW
# define EMULATE_WRITEV
#endif

// Include the necessary system header when using the system's writev(2).
#ifndef EMULATE_WRITEV
# define _XOPEN_SOURCE // Unlock IOV_MAX
# include <sys/uio.h>
#endif

#include <stdbool.h>
#include <stdlib.h>
Expand All @@ -8,7 +23,7 @@
#include <getopt.h>
#include <errno.h>
#include <limits.h>
#include <sys/uio.h>

#include "../include/libbase64.h"

// Size of the buffer for the "raw" (not base64-encoded) data in bytes.
Expand Down Expand Up @@ -50,6 +65,59 @@ struct buffer {
char *enc;
};

// Optionally emulate writev(2) as a series of write calls.
#ifdef EMULATE_WRITEV

// Quick and dirty definition of IOV_MAX as it is probably not defined.
#ifndef IOV_MAX
# define IOV_MAX 1024
#endif

// Quick and dirty definition of this system struct, for local use only.
struct iovec {

// Opaque data pointer.
void *iov_base;

// Length of the data in bytes.
size_t iov_len;
};

static ssize_t
writev (const int fd, const struct iovec *iov, int iovcnt)
{
ssize_t r, nwrite = 0;

// Reset the error marker.
errno = 0;

while (iovcnt-- > 0) {

// Write the vector; propagate errors back to the caller. Note
// that this loses information about how much vectors have been
// successfully written, but that also seems to be the case
// with the real function. The API is somewhat flawed.
if ((r = write(fd, iov->iov_base, iov->iov_len)) < 0) {
return r;
}

// Update the total write count.
nwrite += r;

// Return early after a partial write; the caller should retry.
if ((size_t) r != iov->iov_len) {
break;
}

// Move to the next vector.
iov++;
}

return nwrite;
}

#endif // EMULATE_WRITEV

static bool
buffer_alloc (const struct config *config, struct buffer *buf)
{
Expand Down
4 changes: 3 additions & 1 deletion test/Makefile
@@ -1,11 +1,13 @@
CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic
CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic -DBASE64_STATIC_DEFINE
ifdef OPENMP
CFLAGS += -fopenmp
endif

TARGET := $(shell $(CC) -dumpmachine)
ifneq (, $(findstring darwin, $(TARGET)))
BENCH_LDFLAGS=
else ifneq (, $(findstring mingw, $(TARGET)))
BENCH_LDFLAGS=
else
# default to linux, -lrt needed
BENCH_LDFLAGS=-lrt
Expand Down

0 comments on commit fff4fb8

Please sign in to comment.