Skip to content
Open
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
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,21 @@
/wimlib-imagex
/wimlib-imagex.exe
/wimlib.pc
.vs
/x86
/win32
/x64
/arm
/arm32
/arm64
/Debug
/Release
*/x86
*/win32
*/x64
*/arm
*/arm32
*/arm64
*/Debug
*/Release
*.vcxproj.user
42 changes: 42 additions & 0 deletions include/msvc/unistd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef _UNISTD_H
#define _UNISTD_H 1

/* This file intended to serve as a drop-in replacement for
* unistd.h on Windows
* Please add functionality as neeeded
*/

#include <stdlib.h>
#include <io.h>
#include <process.h> /* for getpid() and the exec..() family */
#include <direct.h> /* for _getcwd() and _chdir() */

#define srandom srand
#define random rand

/* Values for the second argument to access.
These may be OR'd together. */
#define R_OK 4 /* Test for read permission. */
#define W_OK 2 /* Test for write permission. */
//#define X_OK 1 /* execute permission - unsupported in windows*/
#define F_OK 0 /* Test for existence. */

#define access _access
#define dup2 _dup2
#define execve _execve
#define ftruncate _chsize
#define unlink _unlink
#define fileno _fileno
#define getcwd _getcwd
#define chdir _chdir
#define isatty _isatty
#define lseek _lseek
/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */

#define ssize_t int
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ssize_t should be 64-bit on 64-bit platforms.

You can use the following from MinGW:

#ifndef _SSIZE_T_DEFINED
#define _SSIZE_T_DEFINED
#undef ssize_t
#ifdef _WIN64
typedef __int64 ssize_t;
#else
typedef int ssize_t;
#endif
#endif

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe typedef ptrdiff_t ssize_t; instead?


#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

#endif /* unistd.h */
13 changes: 6 additions & 7 deletions include/wimlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,16 +391,15 @@
#include <stdio.h>
#include <stddef.h>
#ifndef __cplusplus
# if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */
typedef unsigned char bool;
# else
# include <stdbool.h>
# endif
#if defined(_MSC_VER)
#include "msvc/unistd.h"
#endif
# include <stdbool.h>
#endif
#include <stdint.h>
#include <time.h>

#ifdef BUILDING_WIMLIB
#if defined BUILDING_WIMLIB || defined LIBWIM_EXPORTS
/*
* On i386, gcc assumes that the stack is 16-byte aligned at function entry.
* However, some compilers (e.g. MSVC) and programming languages (e.g. Delphi)
Expand All @@ -414,7 +413,7 @@
# else
# define WIMLIB_ALIGN_STACK
# endif
# ifdef _WIN32
#if defined _WIN32 || defined LIBWIM_EXPORTS
# define WIMLIBAPI __declspec(dllexport) WIMLIB_ALIGN_STACK
# else
# define WIMLIBAPI __attribute__((visibility("default"))) WIMLIB_ALIGN_STACK
Expand Down
131 changes: 126 additions & 5 deletions include/wimlib/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@
* evaluates to 1. The other evaluates to 0. Note that newer gcc supports
* __BYTE_ORDER__ for easily determining the endianness; older gcc doesn't. In
* the latter case we fall back to a configure-time check. */
#ifdef _MSC_VER
#define CPU_IS_BIG_ENDIAN() 0
#include<assert.h>
#endif
#ifdef __BYTE_ORDER__
# define CPU_IS_BIG_ENDIAN() (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined(HAVE_CONFIG_H)
Expand All @@ -110,15 +114,23 @@

/* Get the minimum of two variables, without multiple evaluation. */
#undef min
#ifdef _MSC_VER
#define min(a, b) ((a < b) ? a : b)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: MSVC's stdlib.h provides min() and max() so you can avoid this redef by simply including stdlib.h

#else
#define min(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \
(_a < _b) ? _a : _b; })
#endif
#undef MIN
#define MIN(a, b) min((a), (b))

/* Get the maximum of two variables, without multiple evaluation. */
#undef max
#ifdef _MSC_VER
#define max(a, b) ((a > b) ? a : b)
#else
#define max(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \
(_a > _b) ? _a : _b; })
#endif
#undef MAX
#define MAX(a, b) max((a), (b))

Expand All @@ -127,10 +139,13 @@
#define max3(a, b, c) max(max((a), (b)), (c))

/* Swap the values of two variables, without multiple evaluation. */
#ifdef _MSC_VER
# define swap(a, b, type) { type _a = (a); (a) = (b); (b) = _a; }
#endif
#ifndef swap
# define swap(a, b) ({ typeof(a) _a = (a); (a) = (b); (b) = _a; })
Copy link

@pbatard pbatard Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest Visual Studio supports typeof() when compiling with the /std:clatest flag, so you can avoid the whole dance around typeof() and keep the swap(a, b) as it is. This also applies to other uses of typeof() in the codebase.

For this specific macro, we can also use the more universally compatible:

#  define swap(a, b) do { typeof(a) _a = (a); (a) = (b); (b) = _a; } while(0)

# define swap(a, b, type) ({ typeof(a) _a = (a); (a) = (b); (b) = _a; })
#endif
#define SWAP(a, b) swap((a), (b))
#define SWAP(a, b ,type) swap((a), (b),type)

/* Optional definitions for checking with 'sparse'. */
#ifdef __CHECKER__
Expand All @@ -142,21 +157,127 @@
#endif

/* STATIC_ASSERT() - verify the truth of an expression at compilation time. */
#ifdef _MSC_VER
#define STATIC_ASSERT(expr) assert(expr)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current Visual Studio (even without /std:clatest) defines an __STDC_VERSION__ that is higher 201112L and it has support for _Static_assert(), so this redef can be avoided (especially as you really do want static asserts to work when introducing support for a new compiler).

#else
#ifdef __CHECKER__
# define STATIC_ASSERT(expr)
#elif __STDC_VERSION__ >= 201112L
# define STATIC_ASSERT(expr) _Static_assert((expr), "")
#else
# define STATIC_ASSERT(expr) ((void)sizeof(char[1 - 2 * !(expr)]))
#endif

#endif
/* STATIC_ASSERT_ZERO() - verify the truth of an expression at compilation time
* and also produce a result of value '0' to be used in constant expressions */
#ifdef _MSC_VER
#pragma comment(lib, "ntdll")
#define STATIC_ASSERT_ZERO(expr) 0
#else
#define STATIC_ASSERT_ZERO(expr) ((int)sizeof(char[-!(expr)]))

#endif
#define CONCAT_IMPL(s1, s2) s1##s2

/* CONCAT() - concatenate two tokens at preprocessing time. */
#define CONCAT(s1, s2) CONCAT_IMPL(s1, s2)
#ifndef PACKAGE_VERSION
#define PACKAGE_VERSION ""
#endif
#ifndef PACKAGE_BUGREPORT
#define PACKAGE_BUGREPORT ""
#endif
#ifdef _MSC_VER
//We are using Microsoft's MSVC, gcc specific functions are not available
#include<assert.h>
#define __attribute__(x)
#define POINTER_FIX() (size_t)
#define smart_array(type, name, size) type * name=_alloca((size)*(sizeof(type)))
#define restrict
#define EMPTY 0
#define FILE_SHARE_VALID_FLAGS FILE_SHARE_DELETE | FILE_SHARE_READ |FILE_SHARE_WRITE
#include<wchar.h>
static inline wchar_t * wmempcpy(wchar_t *_S1, wchar_t const *_S2, size_t _N)
{
return wmemcpy(_S1,_S2,_N)+_N;

}
#define alloca _alloca

#include <intrin.h>
#include <stdint.h>
uint32_t __inline __builtin_ctz(uint32_t value)
{
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value))
return trailing_zero;
return 32;
}

#endif /* _WIMLIB_COMPILER_H */
uint32_t __inline __builtin_clz(uint32_t value)
{
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value))
return 31 - leading_zero;
return 32;
}
#define __builtin_constant_p(x) 0
#define __builtin_prefetch(x, y) 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefetch is available for MSVC on x86 (but not on ARM) so you should be able to use:

#if defined(_M_IX86) || defined(_M_X64)
#define prefetchr		_m_prefetch
#define prefetchw		_m_prefetchw
#else
#define prefetchr(x)
#define prefetchw(x)
#endif

#define __builtin_expect(x,y) (x)
#define gmtime_r(x, y) gmtime_s(y,x)
#define vsnwprintf _vsnwprintf
#define snwprintf _snwprintf
#undef PRIu64
#define PRIu64 "I64u"
#undef PRId64
#define PRId64 "I64d"
#undef PRIi64
#define PRIi64 "I64i"
#undef PRIo64
#define PRIo64 "I64o"
#undef PRIx64
#define PRIx64 "I64x"
#if defined(_M_ARM64) || defined(_M_X64)
uint32_t __inline __builtin_clzll(uint64_t value)
{
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value))
return 63 - leading_zero;
return 64;
}
uint32_t __inline __builtin_ctzll(uint32_t value)
Copy link

@pbatard pbatard Apr 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ctzll works on 64-bit so it should be uint64_t value here, not uint32_t value.

{
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value))
return trailing_zero;
return 64;
}
#else
uint32_t __inline __builtin_clzll(uint64_t value)
{
if (value == 0)
return 64;
uint32_t msh = (uint32_t)(value >> 32);
uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
if (msh != 0)
return __builtin_clz(msh);
return 32 + __builtin_clz(lsh);
}
uint32_t __inline __builtin_ctzll(uint64_t value)
{
if (value == 0)
return 64;
uint32_t msh = (uint32_t)(value >> 32);
uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
if (msh != 0)
return __builtin_ctz(msh);
return 32 + __builtin_ctz(lsh);
}
#endif
#define __builtin_clzl __builtin_clzll
#else
#define POINTER_FIX()
#define smart_array(type, name, size) type name[size]
#define EMPTY
#endif
#endif
/* _WIMLIB_COMPILER_H */
9 changes: 7 additions & 2 deletions include/wimlib/decompress_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,17 +400,22 @@ read_huffsym(struct input_bitstream *is, const u16 decode_table[],
* structure, then the outer structure must be allocated on a
* DECODE_TABLE_ALIGNMENT-byte aligned boundary as well.
*/
#ifdef _MSC_VER
#pragma pack(push, 16)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better to define PRAGMA_BEGIN_PACKED(x) / PRAGMA_END_PACKED macros, as this could reduce all this 3 line boilerplate to a single less conspicuous line.

#endif
#define DECODE_TABLE(name, num_syms, table_bits, max_codeword_len) \
u16 name[DECODE_TABLE_SIZE((num_syms), (table_bits), \
(max_codeword_len))] \
__attribute__((aligned(DECODE_TABLE_ALIGNMENT)))

#ifdef _MSC_VER
#pragma pack(pop)
#endif
/*
* Declare the temporary "working_space" array needed for building the decode
* table for a Huffman code.
*/
#define DECODE_TABLE_WORKING_SPACE(name, num_syms, max_codeword_len) \
u16 name[2 * ((max_codeword_len) + 1) + (num_syms)];
u16 name[2 * ((max_codeword_len) + 1) + (num_syms)]

int
make_huffman_decode_table(u16 decode_table[], unsigned num_syms,
Expand Down
10 changes: 5 additions & 5 deletions include/wimlib/file_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,27 @@
struct filedes {
int fd;
unsigned int is_pipe : 1;
off_t offset;
Copy link

@pbatard pbatard Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid this whole patch by adding the following in the MSVC preprocessor definitions:

_OFF_T_DEFINED;_off_t=__int64;off_t=_off_t;_FILE_OFFSET_BITS=64;

If you do just that, then off_t will be 64-bit, even when compiling for x86.

uint64_t offset;
};

int
full_read(struct filedes *fd, void *buf, size_t n);

int
full_pread(struct filedes *fd, void *buf, size_t nbyte, off_t offset);
full_pread(struct filedes *fd, void *buf, size_t nbyte, uint64_t offset);

int
full_write(struct filedes *fd, const void *buf, size_t n);

int
full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset);
full_pwrite(struct filedes *fd, const void *buf, size_t count, uint64_t offset);

#ifndef _WIN32
# define O_BINARY 0
#endif

off_t
filedes_seek(struct filedes *fd, off_t offset);
uint64_t
filedes_seek(struct filedes *fd, uint64_t offset);

bool
filedes_is_seekable(struct filedes *fd);
Expand Down
6 changes: 6 additions & 0 deletions include/wimlib/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
((u64)'\0' << 54))

/* On-disk format of the WIM header. */
#ifdef _MSC_VER
#pragma pack(push,1)
#endif
struct wim_header_disk {

/* +0x00: Magic characters WIM_MAGIC or PWM_MAGIC. */
Expand Down Expand Up @@ -112,6 +115,9 @@ struct wim_header_disk {
/* +0xd0 (208) */
} __attribute__((packed));

#ifdef _MSC_VER
#pragma pack(pop)
#endif
/*
* Arbitrarily limit the maximum number of images to 65535, to prevent huge
* memory allocations when processing fuzzed files. This can be increased if
Expand Down
13 changes: 9 additions & 4 deletions include/wimlib/inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,17 @@ struct wim_inode {
struct wim_inode *i_corresponding;
#endif
};

#ifdef _MSC_VER
#pragma pack(push, 8)
#endif
/* Optional extra data for a WIM inode */
struct wim_inode_extra {
size_t size; /* Size of the extra data in bytes */
u8 data[] __attribute__((aligned(8))); /* The extra data */
};

#ifdef _MSC_VER
#pragma pack(pop)
#endif
/*
* The available reparse tags are documented at
* https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c8e77b37-3909-4fe6-a4ea-2b9d423b1ee4
Expand Down Expand Up @@ -268,8 +272,9 @@ struct wim_inode *
new_inode(struct wim_dentry *dentry, bool set_timestamps);

/* Iterate through each alias of the specified inode. */
#define inode_for_each_dentry(dentry, inode) \
hlist_for_each_entry((dentry), &(inode)->i_alias_list, d_alias_node)

#define inode_for_each_dentry(dentry, inode, type) \
hlist_for_each_entry((dentry), &(inode)->i_alias_list, d_alias_node, type)

/* Return an alias of the specified inode. */
#define inode_any_dentry(inode) \
Expand Down
4 changes: 2 additions & 2 deletions include/wimlib/integrity.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ read_integrity_table(WIMStruct *wim, u64 num_checked_bytes,

int
write_integrity_table(WIMStruct *wim,
off_t new_blob_table_end,
off_t old_blob_table_end,
uint64_t new_blob_table_end,
uint64_t old_blob_table_end,
struct integrity_table *old_table);

int
Expand Down
Loading