From b8e975170f0637912d228ff392d53d2dc6f18580 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Wed, 22 Jul 2026 23:01:50 -0700 Subject: [PATCH 01/10] Fix std::min syntax for windows --- src/remfile_vfd.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remfile_vfd.cpp b/src/remfile_vfd.cpp index a943dd7..bc89db5 100644 --- a/src/remfile_vfd.cpp +++ b/src/remfile_vfd.cpp @@ -304,7 +304,7 @@ bool load_chunk(RemFile *f, uint64_t chunk_index, uint64_t chunks_needed) if (i * min_chunk >= data.size()) break; size_t piece_start = i * min_chunk; - size_t piece_end = std::min(piece_start + min_chunk, data.size()); + size_t piece_end = (std::min)(piece_start + min_chunk, data.size()); f->chunks[chunk_index + i] = std::vector(data.begin() + static_cast(piece_start), data.begin() + static_cast(piece_end)); f->chunk_order.push_back(chunk_index + i); @@ -348,7 +348,7 @@ bool remfile_read_bytes(RemFile *f, uint64_t position, size_t size, void *buf) for (uint64_t ci = chunk_start_index; ci <= chunk_end_index; ci++) { const std::vector &chunk = f->chunks[ci]; size_t chunk_offset = (ci == chunk_start_index) ? static_cast(position % min_chunk) : 0; - size_t chunk_length = std::min(chunk.size() - chunk_offset, size - written); + size_t chunk_length = (std::min)(chunk.size() - chunk_offset, size - written); memcpy(out + written, chunk.data() + chunk_offset, chunk_length); written += chunk_length; } From c3fa0b4395155514617755a923cfc5fcefec49fe Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Wed, 22 Jul 2026 23:08:30 -0700 Subject: [PATCH 02/10] Add windows to the CI tests --- .github/workflows/build.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2463bec..9921f8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,8 @@ jobs: - {os: ubuntu-latest, hdf5: '2.1'} - {os: macos-latest, hdf5: '1.14'} - {os: macos-latest, hdf5: '2.1'} + - {os: windows-latest, hdf5: '1.14'} + - {os: windows-latest, hdf5: '2.1'} steps: - name: Checkout @@ -61,7 +63,12 @@ jobs: run: ctest --test-dir build --output-on-failure - name: Run the DANDI smoke test (reads a real NWB file over HTTPS) - run: ./build/test_remfile + run: | + if [ "${{ matrix.os }}" = "windows-latest" ]; then + ./build/Release/test_remfile.exe + else + ./build/test_remfile + fi - name: Check installation and find_package consumption run: | @@ -85,8 +92,12 @@ jobs: EOF cmake -B build -DCMAKE_PREFIX_PATH="$PWD/../install;$CONDA_PREFIX" \ -DHDF5_ROOT="$CONDA_PREFIX" - cmake --build build - ./build/consumer + cmake --build build --config Release + if [ "${{ matrix.os }}" = "windows-latest" ]; then + ./build/Release/consumer.exe + else + ./build/consumer + fi # The dynamically loadable VFD plugin requires HDF5 >= 1.14. Verify it can be # loaded by name via HDF5_PLUGIN_PATH, with nothing linked against remfile. From b08bfcfe42a9fb2170d54adb2b6b1c9e972985cf Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Wed, 22 Jul 2026 23:11:35 -0700 Subject: [PATCH 03/10] Use std::next to advance pointer --- src/remfile_vfd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remfile_vfd.cpp b/src/remfile_vfd.cpp index bc89db5..84165eb 100644 --- a/src/remfile_vfd.cpp +++ b/src/remfile_vfd.cpp @@ -306,7 +306,7 @@ bool load_chunk(RemFile *f, uint64_t chunk_index, uint64_t chunks_needed) size_t piece_start = i * min_chunk; size_t piece_end = (std::min)(piece_start + min_chunk, data.size()); f->chunks[chunk_index + i] = - std::vector(data.begin() + static_cast(piece_start), data.begin() + static_cast(piece_end)); + std::vector(std::next(data.begin(), piece_start), std::next(data.begin(), piece_end)); f->chunk_order.push_back(chunk_index + i); } } From 429f63ab3ba0fb9f3115a5d9aff4a8a77e595d1f Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Wed, 22 Jul 2026 23:22:43 -0700 Subject: [PATCH 04/10] Fix build error --- src/remfile_vfd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remfile_vfd.cpp b/src/remfile_vfd.cpp index 84165eb..fdc6b7a 100644 --- a/src/remfile_vfd.cpp +++ b/src/remfile_vfd.cpp @@ -306,7 +306,7 @@ bool load_chunk(RemFile *f, uint64_t chunk_index, uint64_t chunks_needed) size_t piece_start = i * min_chunk; size_t piece_end = (std::min)(piece_start + min_chunk, data.size()); f->chunks[chunk_index + i] = - std::vector(std::next(data.begin(), piece_start), std::next(data.begin(), piece_end)); + std::vector(data.data() + piece_start, data.data() + piece_end); f->chunk_order.push_back(chunk_index + i); } } From 770fa2ca8b8ea4e9c573cb7fc3ddc23791deb641 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Wed, 22 Jul 2026 23:47:01 -0700 Subject: [PATCH 05/10] Attempt to fix windows build --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9921f8b..f29c1d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,9 +52,11 @@ jobs: # it only searches system paths otherwise. Warnings are errors in CI # only, so new compiler versions don't break downstream builds. run: | - cmake -B build -DCMAKE_BUILD_TYPE=Release \ + conda run -n remfile cmake -B build -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_COMPILE_WARNING_AS_ERROR=ON \ - -DCMAKE_PREFIX_PATH="$CONDA_PREFIX" -DHDF5_ROOT="$CONDA_PREFIX" + -DCMAKE_PREFIX_PATH="$CONDA_PREFIX" \ + -DHDF5_ROOT="$CONDA_PREFIX" \ + -DHDF5_USE_STATIC_LIBRARIES=OFF - name: Build run: cmake --build build --config Release -j 2 From e2346a86f15ee7930cf13ed8908379ef7b65cdd0 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Wed, 22 Jul 2026 23:52:14 -0700 Subject: [PATCH 06/10] Attempt to fix windows build --- .github/workflows/build.yml | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f29c1d3..1ed550d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,17 +52,22 @@ jobs: # it only searches system paths otherwise. Warnings are errors in CI # only, so new compiler versions don't break downstream builds. run: | - conda run -n remfile cmake -B build -DCMAKE_BUILD_TYPE=Release \ + if [ "$RUNNER_OS" = "Windows" ]; then + export PREFIX_PATH="$CONDA_PREFIX/Library" + else + export PREFIX_PATH="$CONDA_PREFIX" + fi + cmake -B build -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_COMPILE_WARNING_AS_ERROR=ON \ - -DCMAKE_PREFIX_PATH="$CONDA_PREFIX" \ - -DHDF5_ROOT="$CONDA_PREFIX" \ + -DCMAKE_PREFIX_PATH="$PREFIX_PATH" \ + -DHDF5_ROOT="$PREFIX_PATH" \ -DHDF5_USE_STATIC_LIBRARIES=OFF - name: Build run: cmake --build build --config Release -j 2 - name: Run the test suite (hermetic — no network) - run: ctest --test-dir build --output-on-failure + run: ctest --test-dir build -C Release --output-on-failure - name: Run the DANDI smoke test (reads a real NWB file over HTTPS) run: | @@ -74,7 +79,12 @@ jobs: - name: Check installation and find_package consumption run: | - cmake --install build --prefix "$PWD/install" + if [ "$RUNNER_OS" = "Windows" ]; then + export PREFIX_PATH="$CONDA_PREFIX/Library" + else + export PREFIX_PATH="$CONDA_PREFIX" + fi + cmake --install build --config Release --prefix "$PWD/install" # A downstream project must be able to find and link remfile::remfile mkdir -p consumer && cd consumer cat > CMakeLists.txt <<'EOF' @@ -92,8 +102,8 @@ jobs: return H5Pset_fapl_remfile(fapl, NULL) < 0 ? 1 : 0; } EOF - cmake -B build -DCMAKE_PREFIX_PATH="$PWD/../install;$CONDA_PREFIX" \ - -DHDF5_ROOT="$CONDA_PREFIX" + cmake -B build -DCMAKE_PREFIX_PATH="$PWD/../install;$PREFIX_PATH" \ + -DHDF5_ROOT="$PREFIX_PATH" -DHDF5_USE_STATIC_LIBRARIES=OFF cmake --build build --config Release if [ "${{ matrix.os }}" = "windows-latest" ]; then ./build/Release/consumer.exe From e935b38223bc5ca1d739aeaa38f57560996bed44 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Wed, 22 Jul 2026 23:59:20 -0700 Subject: [PATCH 07/10] fix timing test on windows --- tests/test_remfile.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_remfile.c b/tests/test_remfile.c index 4b68aa7..9beb088 100644 --- a/tests/test_remfile.c +++ b/tests/test_remfile.c @@ -12,6 +12,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + #include #include @@ -22,9 +26,16 @@ static const char *default_dataset = static double now_seconds(void) { +#ifdef _WIN32 + LARGE_INTEGER count, freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&freq); + return (double)count.QuadPart / (double)freq.QuadPart; +#else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; +#endif } /* H5Literate2 / H5L_info2_t are only available from HDF5 1.12 onwards. */ From 55a3fe01692857b3b79379d144374c6548c80ca7 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 23 Jul 2026 00:04:47 -0700 Subject: [PATCH 08/10] Attempt to fix windows build --- tests/http_test_server.hpp | 54 ++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/tests/http_test_server.hpp b/tests/http_test_server.hpp index 1c86347..b860db2 100644 --- a/tests/http_test_server.hpp +++ b/tests/http_test_server.hpp @@ -20,10 +20,22 @@ #ifndef REMFILE_HTTP_TEST_SERVER_HPP #define REMFILE_HTTP_TEST_SERVER_HPP +#ifdef _WIN32 +#include +#include +#pragma comment(lib, "ws2_32.lib") +typedef int socklen_t; +typedef SOCKET socket_t; +#define SHUT_RDWR SD_BOTH +#define close_socket closesocket +#else #include #include #include #include +typedef int socket_t; +#define close_socket ::close +#endif #include #include @@ -43,12 +55,16 @@ class HttpTestServer explicit HttpTestServer(std::vector data) : m_data(std::move(data)) { +#ifdef _WIN32 + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif m_listen_fd = ::socket(AF_INET, SOCK_STREAM, 0); - if (m_listen_fd < 0) + if (m_listen_fd == (socket_t)-1) throw std::runtime_error("HttpTestServer: socket() failed"); int yes = 1; - ::setsockopt(m_listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + ::setsockopt(m_listen_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes)); sockaddr_in addr {}; addr.sin_family = AF_INET; @@ -70,7 +86,7 @@ class HttpTestServer { m_stop = true; ::shutdown(m_listen_fd, SHUT_RDWR); - ::close(m_listen_fd); + close_socket(m_listen_fd); if (m_thread.joinable()) m_thread.join(); @@ -85,12 +101,16 @@ class HttpTestServer * the failure. */ { std::lock_guard lock(m_conn_mutex); - for (int fd : m_conn_fds) + for (socket_t fd : m_conn_fds) ::shutdown(fd, SHUT_RDWR); } for (auto& t : m_conn_threads) if (t.joinable()) t.join(); + +#ifdef _WIN32 + WSACleanup(); +#endif } HttpTestServer(const HttpTestServer&) = delete; @@ -135,8 +155,8 @@ class HttpTestServer void run() { while (!m_stop) { - int fd = ::accept(m_listen_fd, nullptr, nullptr); - if (fd < 0) { + socket_t fd = ::accept(m_listen_fd, nullptr, nullptr); + if (fd == (socket_t)-1) { if (m_stop) break; continue; @@ -151,19 +171,19 @@ class HttpTestServer [this, fd] { handle_connection(fd); - ::close(fd); + close_socket(fd); }); } } /* Read a full request header block (keep-alive: one connection may carry * several requests, which is exactly what the driver's curl handle does). */ - void handle_connection(int fd) + void handle_connection(socket_t fd) { std::string buf; char chunk[4096]; while (!m_stop) { - ssize_t n = ::recv(fd, chunk, sizeof(chunk), 0); + int n = ::recv(fd, chunk, sizeof(chunk), 0); if (n <= 0) return; buf.append(chunk, (size_t)n); @@ -180,7 +200,7 @@ class HttpTestServer } /* Returns false if the connection should be closed. */ - bool handle_request(int fd, const std::string& request) + bool handle_request(socket_t fd, const std::string& request) { m_request_count++; @@ -190,7 +210,7 @@ class HttpTestServer "HTTP/1.1 500 Internal Server Error\r\n" "Content-Length: 0\r\n" "Connection: keep-alive\r\n\r\n"; - ::send(fd, resp.data(), resp.size(), 0); + ::send(fd, resp.data(), (int)resp.size(), 0); return true; } @@ -199,7 +219,7 @@ class HttpTestServer "HTTP/1.1 404 Not Found\r\n" "Content-Length: 0\r\n" "Connection: keep-alive\r\n\r\n"; - ::send(fd, resp.data(), resp.size(), 0); + ::send(fd, resp.data(), (int)resp.size(), 0); return true; } @@ -226,7 +246,7 @@ class HttpTestServer "HTTP/1.1 416 Range Not Satisfiable\r\n" "Content-Length: 0\r\n" "Connection: keep-alive\r\n\r\n"; - ::send(fd, resp.data(), resp.size(), 0); + ::send(fd, resp.data(), (int)resp.size(), 0); return true; } if (end >= m_data.size()) @@ -259,12 +279,12 @@ class HttpTestServer "Connection: keep-alive\r\n\r\n", (unsigned long long)len); } - if (::send(fd, header, (size_t)hlen, 0) < 0) + if (::send(fd, header, hlen, 0) < 0) return false; size_t sent = 0; while (sent < len) { - ssize_t n = ::send(fd, m_data.data() + start + sent, (size_t)(len - sent), 0); + int n = ::send(fd, (const char*)(m_data.data() + start + sent), (int)(len - sent), 0); if (n <= 0) return false; sent += (size_t)n; @@ -284,7 +304,7 @@ class HttpTestServer } std::vector m_data; - int m_listen_fd = -1; + socket_t m_listen_fd = (socket_t)-1; int m_port = 0; std::thread m_thread; std::atomic m_stop {false}; @@ -297,7 +317,7 @@ class HttpTestServer std::vector> m_ranges; std::mutex m_conn_mutex; std::vector m_conn_threads; - std::vector m_conn_fds; + std::vector m_conn_fds; }; } // namespace remfile_test From 175245f249d8b12ebf6ae3bd866389a1d7b6bcc5 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 23 Jul 2026 00:10:41 -0700 Subject: [PATCH 09/10] Fix small expanding to char on Windows error --- tests/test_remfile_vfd.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_remfile_vfd.cpp b/tests/test_remfile_vfd.cpp index fe6d1bb..9024d48 100644 --- a/tests/test_remfile_vfd.cpp +++ b/tests/test_remfile_vfd.cpp @@ -92,10 +92,10 @@ std::vector build_test_file(const std::string& path) hsize_t sdim = 5; hid_t sspace = H5Screate_simple(1, &sdim, nullptr); - int small[5] = {10, 20, 30, 40, 50}; + int small_data[5] = {10, 20, 30, 40, 50}; dset = H5Dcreate2(file, "small", H5T_NATIVE_INT, sspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - REQUIRE(H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, small) >= 0); + REQUIRE(H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, small_data) >= 0); H5Dclose(dset); hid_t ascalar = H5Screate(H5S_SCALAR); @@ -237,10 +237,10 @@ TEST_CASE("attributes and small datasets read correctly", "[vfd]") hid_t dset = H5Dopen2(file, "small", H5P_DEFAULT); REQUIRE(dset >= 0); - int small[5] = {0}; - REQUIRE(H5Dread(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, small) >= 0); - REQUIRE(small[0] == 10); - REQUIRE(small[4] == 50); + int small_data[5] = {0}; + REQUIRE(H5Dread(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, small_data) >= 0); + REQUIRE(small_data[0] == 10); + REQUIRE(small_data[4] == 50); H5Dclose(dset); H5Fclose(file); } From 2bf405e0b88b2e34a27d2a7151b41d4d866aba8a Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 23 Jul 2026 00:25:39 -0700 Subject: [PATCH 10/10] Next fix for Windows build --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1ed550d..51dc8e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -102,7 +102,7 @@ jobs: return H5Pset_fapl_remfile(fapl, NULL) < 0 ? 1 : 0; } EOF - cmake -B build -DCMAKE_PREFIX_PATH="$PWD/../install;$PREFIX_PATH" \ + cmake -B build -DCMAKE_PREFIX_PATH="../install;$PREFIX_PATH" \ -DHDF5_ROOT="$PREFIX_PATH" -DHDF5_USE_STATIC_LIBRARIES=OFF cmake --build build --config Release if [ "${{ matrix.os }}" = "windows-latest" ]; then