SWMMVis  6.0.0-alpha.4
Qt6/C++ GIS-based graphical user interface for the SWMMVis engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
file.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2#pragma once
3#include "meshkit/io.hpp"
4#include "operation.hpp"
5#include <array>
6#include <atomic>
7#include <cstdio>
8#include <cerrno>
9#include <cstring>
10#include <random>
11#ifdef _WIN32
12#ifndef NOMINMAX
13#define NOMINMAX
14#endif
15#include <windows.h>
16#include <io.h>
17#else
18#include <unistd.h>
19#endif
21{
22inline bool validPath(const std::filesystem::path &p)
23{
24 return !p.empty() && p.native().find(std::filesystem::path::value_type{}) ==
25 std::filesystem::path::string_type::npos;
26}
27constexpr std::uint64_t hashSeed = 14695981039346656037ull;
28inline void hashBytes(std::uint64_t &hash, const unsigned char *p, std::size_t n)
29{
30 for (std::size_t i = 0; i < n; ++i) {
31 hash ^= p[i];
32 hash *= 1099511628211ull;
33 }
34}
36{
37 std::filesystem::path target_, temp_;
38 bool overwrite_, durable_;
39 std::FILE *file_ = nullptr;
40
41public:
42 Transaction(const std::filesystem::path &target, const IOOptions &options)
43 : target_(target), overwrite_(options.overwrite), durable_(options.durable)
44 {
45 if (!validPath(target) || target.filename().empty() || target.filename() == "." ||
46 target.filename() == "..")
47 fail("path", "Invalid output path.", RunStatus::Failed);
48 auto parent = target.parent_path();
49 if (parent.empty())
50 parent = ".";
51 static std::atomic<std::uint64_t> sequence{0};
52 std::random_device random;
53 for (int attempt = 0; attempt < 32; ++attempt) {
54 const auto nonce = (std::uint64_t(random()) << 32) ^ random() ^ ++sequence;
55 temp_ = parent / (".meshkit-" + std::to_string(nonce) + ".tmp");
56#ifdef _WIN32
57 file_ = _wfopen(temp_.c_str(), L"wbx");
58#else
59 file_ = std::fopen(temp_.c_str(), "wbx");
60#endif
61 if (file_)
62 return;
63 if (errno != EEXIST)
64 break;
65 }
66 fail("open_output",
67 "Cannot create an exclusive output temporary: " + std::string(std::strerror(errno)),
69 }
71 {
72 if (file_)
73 std::fclose(file_);
74 if (!temp_.empty()) {
75 std::error_code error;
76 std::filesystem::remove(temp_, error);
77 }
78 }
79 Transaction(const Transaction &) = delete;
80 std::FILE *file() const { return file_; }
81 void commit()
82 {
83 if (std::fflush(file_) != 0)
84 fail("write", "Cannot flush output.", RunStatus::Failed);
85 if (durable_) {
86#ifdef _WIN32
87 if (_commit(_fileno(file_)) != 0)
88 fail("sync", "Cannot synchronize output.", RunStatus::Failed);
89#else
90 if (::fsync(fileno(file_)) != 0)
91 fail("sync", "Cannot synchronize output.", RunStatus::Failed);
92#endif
93 }
94 const int closed = std::fclose(file_);
95 file_ = nullptr;
96 if (closed != 0)
97 fail("close", "Cannot close output.", RunStatus::Failed);
98#ifdef _WIN32
99 const DWORD flags =
100 (overwrite_ ? MOVEFILE_REPLACE_EXISTING : 0) | (durable_ ? MOVEFILE_WRITE_THROUGH : 0);
101 if (!MoveFileExW(temp_.c_str(), target_.c_str(), flags))
102 fail("publish", "Cannot atomically publish output (destination may already exist).",
104#else
105 if (overwrite_) {
106 if (::rename(temp_.c_str(), target_.c_str()) != 0)
107 fail("publish",
108 "Cannot atomically replace output: " + std::string(std::strerror(errno)),
110 } else {
111 if (::link(temp_.c_str(), target_.c_str()) != 0)
112 fail("publish",
113 "Cannot publish output without replacing an existing file: " +
114 std::string(std::strerror(errno)),
116 ::unlink(temp_.c_str());
117 }
118#endif
119 temp_.clear();
120 }
121};
123{
124 std::FILE *file_;
125 Work &work_;
126 std::array<unsigned char, 65536> buffer_{};
127 std::size_t used_ = 0;
128
129public:
130 std::uint64_t bytes = 0, hash = hashSeed;
131 Output(std::FILE *file, Work &work) : file_(file), work_(work) {}
132 void flush()
133 {
134 work_.check();
135 if (used_ && std::fwrite(buffer_.data(), 1, used_, file_) != used_)
136 fail("write", "Output write failed.", RunStatus::Failed);
137 used_ = 0;
138 }
139 void raw(const void *data, std::size_t n, bool hashed = true)
140 {
141 auto p = static_cast<const unsigned char *>(data);
142 if (hashed)
143 hashBytes(hash, p, n);
144 bytes += n;
145 while (n) {
146 const auto count = std::min(n, buffer_.size() - used_);
147 std::memcpy(buffer_.data() + used_, p, count);
148 used_ += count;
149 p += count;
150 n -= count;
151 if (used_ == buffer_.size())
152 flush();
153 }
154 }
155 void number(std::uint64_t value, int width, bool hashed = true)
156 {
157 unsigned char data[8];
158 for (int i = 0; i < width; ++i) {
159 data[i] = value & 255;
160 value >>= 8;
161 }
162 raw(data, width, hashed);
163 }
164 void real(double value)
165 {
166 std::uint64_t bits;
167 static_assert(sizeof(value) == sizeof(bits));
168 std::memcpy(&bits, &value, 8);
169 number(bits, 8);
170 }
171 void text(const std::string &s)
172 {
173 number(s.size(), 8);
174 raw(s.data(), s.size());
175 }
176};
177class Input
178{
179 std::istream &stream_;
180 Work &work_;
181 std::array<unsigned char, 65536> buffer_{};
182 std::size_t at_ = 0, end_ = 0;
183
184public:
185 std::uint64_t bytes = 0, hash = hashSeed;
186 Input(std::istream &stream, Work &work) : stream_(stream), work_(work) {}
187 bool byte(unsigned char &value, bool hashed = true)
188 {
189 if (at_ == end_) {
190 work_.check();
191 stream_.read(reinterpret_cast<char *>(buffer_.data()), buffer_.size());
192 end_ = std::size_t(stream_.gcount());
193 at_ = 0;
194 if (!end_) {
195 if (stream_.bad())
196 fail("read", "Input read failed.", RunStatus::Failed);
197 return false;
198 }
199 }
200 value = buffer_[at_++];
201 ++bytes;
202 if (hashed)
203 hashBytes(hash, &value, 1);
204 return true;
205 }
206 void raw(void *target, std::size_t n, bool hashed = true)
207 {
208 auto p = static_cast<unsigned char *>(target);
209 for (std::size_t i = 0; i < n; ++i)
210 if (!byte(p[i], hashed))
211 fail("truncated", "Truncated input.", RunStatus::Failed);
212 }
213 std::uint64_t number(int width, bool hashed = true)
214 {
215 unsigned char data[8];
216 raw(data, width, hashed);
217 std::uint64_t value = 0;
218 for (int i = 0; i < width; ++i)
219 value |= std::uint64_t(data[i]) << (8 * i);
220 return value;
221 }
222 double real()
223 {
224 const auto bits = number(8);
225 double value;
226 std::memcpy(&value, &bits, 8);
227 return value;
228 }
229 std::string text(std::uint64_t &remaining)
230 {
231 auto n = number(8);
232 if (n > remaining)
233 fail("metadata_limit", "Metadata size exceeds the limit.", RunStatus::ResourceLimit);
234 remaining -= n;
235 std::string result(std::size_t(n), '\0');
236 raw(result.data(), result.size());
237 return result;
238 }
239};
240inline void appendId(std::vector<std::int64_t> &values, std::int64_t id, std::size_t index)
241{
242 if (values.empty() && id == std::int64_t(index + 1))
243 return;
244 if (values.empty()) {
245 values.reserve(index + 1);
246 for (std::size_t i = 0; i < index; ++i)
247 values.push_back(i + 1);
248 }
249 values.push_back(id);
250}
251inline void appendMarker(std::vector<std::int32_t> &values, std::int32_t marker, std::size_t index)
252{
253 if (!marker && values.empty())
254 return;
255 if (values.empty())
256 values.resize(index, 0);
257 values.push_back(marker);
258}
259inline std::int64_t externalId(const std::vector<std::int64_t> &ids, std::size_t index)
260{
261 return ids.empty() ? std::int64_t(index + 1) : ids[index];
262}
263inline std::int64_t signed64(std::uint64_t bits)
264{
265 std::int64_t value;
266 std::memcpy(&value, &bits, 8);
267 return value;
268}
269inline std::int32_t signed32(std::uint64_t bits)
270{
271 std::uint32_t raw = std::uint32_t(bits);
272 std::int32_t value;
273 std::memcpy(&value, &raw, 4);
274 return value;
275}
276inline void setError(IOReport &r, const Stop &e)
277{
278 r.status = e.status;
279 r.code = e.code;
280 r.message = e.message;
281}
282inline void setException(IOReport &r, const std::bad_alloc &)
283{
285 r.code = "allocation";
286 r.message = "Insufficient memory.";
287}
288inline void setException(IOReport &r, const std::exception &e)
289{
291 r.code = "exception";
292 r.message = e.what();
293}
294inline void validated(const Mesh &mesh, const IOOptions &o)
295{
296 const auto r = validateMesh(mesh, o.limits, o.cancelled);
297 if (!r.complete())
298 fail(r.code, r.message, r.status);
299}
300}
Definition file.hpp:178
std::uint64_t number(int width, bool hashed=true)
Definition file.hpp:213
void raw(void *target, std::size_t n, bool hashed=true)
Definition file.hpp:206
std::uint64_t hash
Definition file.hpp:185
std::uint64_t bytes
Definition file.hpp:185
bool byte(unsigned char &value, bool hashed=true)
Definition file.hpp:187
double real()
Definition file.hpp:222
std::string text(std::uint64_t &remaining)
Definition file.hpp:229
Input(std::istream &stream, Work &work)
Definition file.hpp:186
Definition file.hpp:123
void raw(const void *data, std::size_t n, bool hashed=true)
Definition file.hpp:139
void flush()
Definition file.hpp:132
void text(const std::string &s)
Definition file.hpp:171
void number(std::uint64_t value, int width, bool hashed=true)
Definition file.hpp:155
std::uint64_t bytes
Definition file.hpp:130
std::uint64_t hash
Definition file.hpp:130
Output(std::FILE *file, Work &work)
Definition file.hpp:131
void real(double value)
Definition file.hpp:164
Transaction(const Transaction &)=delete
std::FILE * file() const
Definition file.hpp:80
void commit()
Definition file.hpp:81
Transaction(const std::filesystem::path &target, const IOOptions &options)
Definition file.hpp:42
~Transaction()
Definition file.hpp:70
size_t i
Definition contourjob.cpp:30
int link
Definition heatconfigdialog.cpp:65
QString target
Definition initialbedqualitydialog.cpp:51
double s
Scene px per model length unit.
Definition inletdrawingview.cpp:95
ArrowPlacement p
Definition linesymbollayer.cpp:29
std::size_t n
Definition mesh2dresultsexport.cpp:538
Definition meshcommands.h:38
Definition file.hpp:21
constexpr std::uint64_t hashSeed
Definition file.hpp:27
void appendMarker(std::vector< std::int32_t > &values, std::int32_t marker, std::size_t index)
Definition file.hpp:251
std::int64_t externalId(const std::vector< std::int64_t > &ids, std::size_t index)
Definition file.hpp:259
void fail(std::string code, std::string message, RunStatus status=RunStatus::Infeasible)
Definition operation.hpp:17
bool validPath(const std::filesystem::path &p)
Definition file.hpp:22
std::int32_t signed32(std::uint64_t bits)
Definition file.hpp:269
void setException(IOReport &r, const std::bad_alloc &)
Definition file.hpp:282
void hashBytes(std::uint64_t &hash, const unsigned char *p, std::size_t n)
Definition file.hpp:28
void setError(IOReport &r, const Stop &e)
Definition file.hpp:276
void appendId(std::vector< std::int64_t > &values, std::int64_t id, std::size_t index)
Definition file.hpp:240
void validated(const Mesh &mesh, const IOOptions &o)
Definition file.hpp:294
std::int64_t signed64(std::uint64_t bits)
Definition file.hpp:263
Definition blocks.hpp:25
OperationReport validateMesh(const Mesh &, const Limits &={}, const std::function< bool()> &cancelled={})
Definition mesh.cpp:9
const ConstraintAuditOptions & options
Definition constraintaudit.cpp:34
std::uint64_t remaining
Definition constraintaudit.cpp:35
int parent
Definition planning.cpp:199
double value
Definition rasterfluxintegration.cpp:27
std::uint64_t number
Definition smsio.cpp:24
Definition io.hpp:11
Limits limits
Definition io.hpp:12
std::function< bool()> cancelled
Definition io.hpp:13
Definition io.hpp:19
Definition mesh.hpp:48
RunStatus status
Definition mesh.hpp:67
std::string message
Definition mesh.hpp:68
std::string code
Definition mesh.hpp:68
Definition operation.hpp:13
RunStatus status
Definition operation.hpp:14
std::string message
Definition operation.hpp:15
std::string code
Definition operation.hpp:15
Definition operation.hpp:22
double error
Definition terrainerrorfield.cpp:42
quint64 count
Definition terrainerrorfield.cpp:43