From 292f4f4f3bdc650e452652e76be03d39afec4036 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 13 Jun 2017 16:39:17 +0200 Subject: Don't rely on utimensat() utimensat() does not work on all filesystems, in particular on Windows. --- src/MinedMap.cpp | 109 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/src/MinedMap.cpp b/src/MinedMap.cpp index b89f89d..3fd9d94 100644 --- a/src/MinedMap.cpp +++ b/src/MinedMap.cpp @@ -30,6 +30,7 @@ #include "World/Region.hpp" #include +#include #include #include #include @@ -44,7 +45,6 @@ #include #include #include -#include namespace MinedMap { @@ -52,27 +52,6 @@ namespace MinedMap { static const size_t DIM = World::Region::SIZE*World::Chunk::SIZE; -static inline bool operator<(const struct timespec &t1, const struct timespec &t2) { - return (t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec)); -} - -static inline bool operator<=(const struct timespec &t1, const struct timespec &t2) { - return (t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec <= t2.tv_nsec)); -} - -static inline bool operator>(const struct timespec &t1, const struct timespec &t2) { - return (t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec)); -} - -static inline bool operator>=(const struct timespec &t1, const struct timespec &t2) { - return (t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec >= t2.tv_nsec)); -} - -static inline bool operator==(const struct timespec &t1, const struct timespec &t2) { - return (t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec); -} - - static void addChunk(uint32_t image[DIM*DIM], uint8_t lightmap[2*DIM*DIM], size_t X, size_t Z, const World::Chunk *chunk) { World::Chunk::Blocks layer = chunk->getTopLayer(); @@ -87,20 +66,38 @@ static void addChunk(uint32_t image[DIM*DIM], uint8_t lightmap[2*DIM*DIM], size_ } } -static void writeImage(const std::string &output, const uint8_t *data, bool colored, const struct timespec *t) { +static int64_t readStamp(const std::string &filename) { + int64_t v = INT64_MIN; + + std::FILE *f = std::fopen((filename + ".stamp").c_str(), "r"); + if (f) { + std::fscanf(f, "%" SCNd64, &v); + std::fclose(f); + } + + return v; +} + +static void writeStamp(const std::string &filename, int64_t v) { + std::FILE *f = std::fopen((filename + ".stamp").c_str(), "w"); + if (f) { + std::fprintf(f, "%" PRId64, v); + std::fclose(f); + } +} + +static void writeImage(const std::string &output, const uint8_t *data, bool colored, int64_t t) { const std::string tmpfile = output + ".tmp"; try { PNG::write(tmpfile.c_str(), data, DIM, DIM, colored); - struct timespec times[2] = {*t, *t}; - if (utimensat(AT_FDCWD, tmpfile.c_str(), times, 0) < 0) - std::fprintf(stderr, "Warning: failed to set utime on %s: %s\n", tmpfile.c_str(), std::strerror(errno)); - if (std::rename(tmpfile.c_str(), output.c_str()) < 0) { std::fprintf(stderr, "Unable to save %s: %s\n", output.c_str(), std::strerror(errno)); std::remove(tmpfile.c_str()); } + + writeStamp(output, t); } catch (const std::exception& ex) { std::remove(tmpfile.c_str()); @@ -109,18 +106,28 @@ static void writeImage(const std::string &output, const uint8_t *data, bool colo } static void doRegion(const std::string &input, const std::string &output, const std::string &output_light) { - struct stat instat, outstat; + int64_t intime; - if (stat(input.c_str(), &instat) < 0) { - std::fprintf(stderr, "Unable to stat %s: %s\n", input.c_str(), std::strerror(errno)); - return; - } + { + struct stat instat; - if (stat(output.c_str(), &outstat) == 0) { - if (instat.st_mtim <= outstat.st_mtim) { - std::printf("%s is up-to-date.\n", output.c_str()); + if (stat(input.c_str(), &instat) < 0) { + std::fprintf(stderr, "Unable to stat %s: %s\n", input.c_str(), std::strerror(errno)); return; } + + intime = (int64_t)instat.st_mtim.tv_sec * 1000000 + instat.st_mtim.tv_nsec / 1000; + } + + { + struct stat s; + if (stat(output.c_str(), &s) == 0) { + int64_t outtime = readStamp(output); + if (intime <= outtime) { + std::printf("%s is up-to-date.\n", output.c_str()); + return; + } + } } std::printf("Generating %s from %s...\n", output.c_str(), input.c_str()); @@ -134,8 +141,8 @@ static void doRegion(const std::string &input, const std::string &output, const World::Region::visitChunks(input.c_str(), [&] (size_t X, size_t Z, const World::Chunk *chunk) { addChunk(image.get(), lightmap.get(), X, Z, chunk); }); - writeImage(output, reinterpret_cast(image.get()), true, &instat.st_mtim); - writeImage(output_light, lightmap.get(), false, &instat.st_mtim); + writeImage(output, reinterpret_cast(image.get()), true, intime); + writeImage(output_light, lightmap.get(), false, intime); } catch (const std::exception& ex) { std::fprintf(stderr, "Failed to generate %s: %s\n", output.c_str(), ex.what()); @@ -185,27 +192,33 @@ static bool makeMipmap(const std::string &dir, size_t level, size_t x, size_t z, const char *sw = sw_str.c_str(); const char *se = se_str.c_str(); - struct timespec t; - struct stat s; + int64_t t = INT64_MIN; size_t count = 0; for (auto name : {&nw, &ne, &sw, &se}) { + struct stat s; if (stat(*name, &s) < 0) { *name = nullptr; continue; } - if (!count || s.st_mtim > t) - t = s.st_mtim; + int64_t t_part = readStamp(*name); + if (t_part > t) + t = t_part; count++; } std::string output = outdir + formatTileName(x, z, "png"); - if (stat(output.c_str(), &s) == 0) { - ret = true; - if (!count || t <= s.st_mtim) - return ret; + { + struct stat s; + if (stat(output.c_str(), &s) == 0) { + ret = true; + + int64_t outtime = readStamp(output); + if (t <= outtime) + return ret; + } } if (!count) @@ -216,14 +229,12 @@ static bool makeMipmap(const std::string &dir, size_t level, size_t x, size_t z, try { PNG::mipmap(tmpfile.c_str(), DIM, DIM, colored, nw, ne, sw, se); - struct timespec times[2] = {t, t}; - if (utimensat(AT_FDCWD, tmpfile.c_str(), times, 0) < 0) - std::fprintf(stderr, "Warning: failed to set utime on %s: %s\n", tmpfile.c_str(), std::strerror(errno)); - if (std::rename(tmpfile.c_str(), output.c_str()) < 0) { std::fprintf(stderr, "Unable to save %s: %s\n", output.c_str(), std::strerror(errno)); std::remove(tmpfile.c_str()); } + + writeStamp(output, t); } catch (const std::exception& ex) { std::remove(tmpfile.c_str()); -- cgit v1.2.3