34[[noreturn]]
void unsupported(
const char *operation) {
35 throw StateError(ErrorKind::State, std::string(
"source does not support ") + operation);
38[[noreturn]]
void ioFailure(
const char *operation) {
39 throw IoError(ErrorKind::Io, std::string(operation) +
": " + std::strerror(errno));
42class FileInput final :
public InputSource {
44 FileInput(FILE *file,
bool owned) : file_(file), owned_(owned) {
47 explicit FileInput(std::filesystem::path path)
48 : file_(std::fopen(path.string().c_str(),
"rb")), owned_(true),
49 path_(std::move(path)), reopenable_(true) {
54 throw IoError(ErrorKind::Io,
55 path_.empty() ?
"null FILE input" :
56 "unable to open input: " + std::string(std::strerror(errno)),
58 const auto position = tellFile();
59 seekable_ = position.has_value();
60 if (!seekable_) std::clearerr(file_);
62 ~FileInput()
override {
try { close(); }
catch (...) {} }
64 std::size_t
read(
void *data, std::size_t size)
override {
66 const std::size_t count = std::fread(data, 1, size, file_);
67 if (count < size && std::ferror(file_)) ioFailure(
"FILE read failed");
70 bool eof()
const override {
return !file_ || std::feof(file_) != 0; }
71 SourceCapabilities capabilities() const noexcept
override {
72 return {
true,
false, seekable_,
false,
false,
false, reopenable_};
74 std::uint64_t tell()
const override {
76 const auto value = tellFile();
77 if (!value) ioFailure(
"FILE tell failed");
80 void seek(std::uint64_t offset)
override {
82 if (!seekable_) unsupported(
"seek");
84 if (_fseeki64(file_,
static_cast<__int64
>(offset), SEEK_SET) != 0)
86 if (offset >
static_cast<std::uint64_t
>(std::numeric_limits<off_t>::max()) ||
87 fseeko(file_,
static_cast<off_t
>(offset), SEEK_SET) != 0)
89 ioFailure(
"FILE seek failed");
92 void close()
override {
94 if (reopenable_) reopenOffset_ = tell();
95 FILE *closing = file_;
97 if (owned_ && std::fclose(closing) != 0) ioFailure(
"FILE close failed");
99 void reopen()
override {
100 if (!reopenable_) unsupported(
"reopen");
102 file_ = std::fopen(path_.string().c_str(),
"rb");
103 if (!file_) ioFailure(
"FILE reopen failed");
109 void requireOpen()
const {
if (!file_) unsupported(
"access after close"); }
110 std::optional<std::uint64_t> tellFile()
const {
112 const auto value = _ftelli64(file_);
114 const auto value = ftello(file_);
116 if (value < 0)
return std::nullopt;
117 return static_cast<std::uint64_t
>(value);
119 FILE *file_ =
nullptr;
121 bool seekable_ =
false;
122 std::filesystem::path path_;
123 std::uint64_t reopenOffset_ = 0;
124 bool reopenable_ =
false;
127class FileOutput final :
public OutputSink {
129 FileOutput(FILE *file,
bool owned) : file_(file), owned_(owned) {
132 FileOutput(std::filesystem::path path,
bool truncateFile)
133 : file_(std::fopen(path.string().c_str(), truncateFile ?
"wb" :
"r+b")),
134 owned_(true), path_(std::move(path)), reopenable_(true) {
139 throw IoError(ErrorKind::Io,
140 path_.empty() ?
"null FILE output" :
141 "unable to open output: " + std::string(std::strerror(errno)),
143 seekable_ = tellFile().has_value();
144 if (!seekable_) std::clearerr(file_);
146 ~FileOutput()
override {
try { close(); }
catch (...) {} }
148 void write(
const void *data, std::size_t size)
override {
150 if (size && std::fwrite(data, 1, size, file_) != size) ioFailure(
"FILE write failed");
152 SourceCapabilities capabilities() const noexcept
override {
153 return {
false,
true, seekable_, seekable_,
true, seekable_, reopenable_};
155 std::uint64_t tell()
const override {
157 const auto value = tellFile();
158 if (!value) ioFailure(
"FILE tell failed");
161 void seek(std::uint64_t offset)
override {
163 if (!seekable_) unsupported(
"seek");
165 if (_fseeki64(file_,
static_cast<__int64
>(offset), SEEK_SET) != 0)
167 if (offset >
static_cast<std::uint64_t
>(std::numeric_limits<off_t>::max()) ||
168 fseeko(file_,
static_cast<off_t
>(offset), SEEK_SET) != 0)
170 ioFailure(
"FILE seek failed");
172 void truncate(std::uint64_t length)
override {
176 if (_chsize_s(_fileno(file_), length) != 0)
178 if (length >
static_cast<std::uint64_t
>(std::numeric_limits<off_t>::max()) ||
179 ftruncate(fileno(file_),
static_cast<off_t
>(length)) != 0)
181 ioFailure(
"FILE truncate failed");
183 void flush()
override {
185 if (std::fflush(file_) != 0) ioFailure(
"FILE flush failed");
187 void sync()
override {
190 if (_commit(_fileno(file_)) != 0)
192 if (::fsync(fileno(file_)) != 0)
194 ioFailure(
"FILE sync failed");
196 void close()
override {
198 if (reopenable_) reopenOffset_ = tell();
199 FILE *closing = file_;
202 if (std::fclose(closing) != 0) ioFailure(
"FILE close failed");
203 }
else if (std::fflush(closing) != 0) {
204 ioFailure(
"FILE flush failed");
207 void reopen()
override {
208 if (!reopenable_) unsupported(
"reopen");
210 file_ = std::fopen(path_.string().c_str(),
"r+b");
211 if (!file_) ioFailure(
"FILE reopen failed");
217 void requireOpen()
const {
if (!file_) unsupported(
"access after close"); }
218 std::optional<std::uint64_t> tellFile()
const {
220 const auto value = _ftelli64(file_);
222 const auto value = ftello(file_);
224 if (value < 0)
return std::nullopt;
225 return static_cast<std::uint64_t
>(value);
227 FILE *file_ =
nullptr;
229 bool seekable_ =
false;
230 std::filesystem::path path_;
231 std::uint64_t reopenOffset_ = 0;
232 bool reopenable_ =
false;
235class StreamInput final :
public InputSource {
237 explicit StreamInput(std::istream &stream) : stream_(&stream) {
238 const auto position = stream_->tellg();
239 seekable_ = position != std::istream::pos_type(-1);
242 std::size_t
read(
void *data, std::size_t size)
override {
244 stream_->read(
static_cast<char *
>(data),
static_cast<std::streamsize
>(size));
245 const auto count = stream_->gcount();
246 if (stream_->bad())
throw IoError(ErrorKind::Io,
"C++ stream read failed");
247 return static_cast<std::size_t
>(count);
249 bool eof()
const override {
return !stream_ || stream_->eof(); }
250 SourceCapabilities capabilities() const noexcept
override {
251 return {
true,
false, seekable_,
false,
false,
false,
false};
253 std::uint64_t tell()
const override {
255 const auto value = stream_->tellg();
256 if (value == std::istream::pos_type(-1))
throw IoError(ErrorKind::Io,
"C++ stream tell failed");
257 return static_cast<std::uint64_t
>(value);
259 void seek(std::uint64_t offset)
override {
261 if (!seekable_) unsupported(
"seek");
263 stream_->seekg(
static_cast<std::streamoff
>(offset));
264 if (!*stream_)
throw IoError(ErrorKind::Io,
"C++ stream seek failed");
266 void close()
override { stream_ =
nullptr; }
269 void requireOpen()
const {
if (!stream_) unsupported(
"access after close"); }
270 std::istream *stream_ =
nullptr;
271 bool seekable_ =
false;
274class StreamOutput final :
public OutputSink {
276 explicit StreamOutput(std::ostream &stream) : stream_(&stream) {
277 const auto position = stream_->tellp();
278 seekable_ = position != std::ostream::pos_type(-1);
281 void write(
const void *data, std::size_t size)
override {
283 stream_->write(
static_cast<const char *
>(data),
static_cast<std::streamsize
>(size));
284 if (!*stream_)
throw IoError(ErrorKind::Io,
"C++ stream write failed");
286 SourceCapabilities capabilities() const noexcept
override {
287 return {
false,
true, seekable_,
false,
true,
false,
false};
289 std::uint64_t tell()
const override {
291 const auto value = stream_->tellp();
292 if (value == std::ostream::pos_type(-1))
throw IoError(ErrorKind::Io,
"C++ stream tell failed");
293 return static_cast<std::uint64_t
>(value);
295 void seek(std::uint64_t offset)
override {
297 if (!seekable_) unsupported(
"seek");
299 stream_->seekp(
static_cast<std::streamoff
>(offset));
300 if (!*stream_)
throw IoError(ErrorKind::Io,
"C++ stream seek failed");
302 void flush()
override {
305 if (!*stream_)
throw IoError(ErrorKind::Io,
"C++ stream flush failed");
307 void close()
override {
if (stream_) flush(); stream_ =
nullptr; }
310 void requireOpen()
const {
if (!stream_) unsupported(
"access after close"); }
311 std::ostream *stream_ =
nullptr;
312 bool seekable_ =
false;
315class MemoryInput final :
public InputSource {
317 MemoryInput(
const void *data, std::size_t size)
318 : data_(static_cast<const std::uint8_t *>(data)), size_(size) {
319 if (!data_ && size_)
throw StateError(ErrorKind::State,
"null memory input");
321 std::size_t
read(
void *data, std::size_t size)
override {
323 const std::size_t count = std::min(size, size_ - position_);
324 if (count) std::memcpy(data, data_ + position_, count);
328 bool eof()
const override {
return !open_ || position_ == size_; }
329 SourceCapabilities capabilities() const noexcept
override {
330 return {
true,
false,
true,
false,
false,
false,
false};
332 std::uint64_t tell()
const override { requireOpen();
return position_; }
333 void seek(std::uint64_t offset)
override {
335 if (offset > size_)
throw StateError(ErrorKind::State,
"memory input seek is out of range");
336 position_ =
static_cast<std::size_t
>(offset);
338 void close()
override { open_ =
false; }
341 void requireOpen()
const {
if (!open_) unsupported(
"access after close"); }
342 const std::uint8_t *data_ =
nullptr;
343 std::size_t size_ = 0;
344 std::size_t position_ = 0;
348class MemoryOutput final :
public OutputSink {
350 explicit MemoryOutput(std::vector<std::uint8_t> &data) : data_(&data) {}
351 void write(
const void *data, std::size_t size)
override {
353 if (size > std::numeric_limits<std::size_t>::max() - position_)
354 throw LimitError(ErrorKind::Limit,
"memory output size overflow");
355 if (data_->size() < position_ + size) data_->resize(position_ + size);
356 if (size) std::memcpy(data_->data() + position_, data, size);
359 SourceCapabilities capabilities() const noexcept
override {
360 return {
false,
true,
true,
true,
true,
true,
false};
362 std::uint64_t tell()
const override { requireOpen();
return position_; }
363 void seek(std::uint64_t offset)
override {
365 if (offset > data_->size())
throw StateError(ErrorKind::State,
"memory output seek is out of range");
366 position_ =
static_cast<std::size_t
>(offset);
368 void truncate(std::uint64_t length)
override {
370 if (length > std::numeric_limits<std::size_t>::max())
371 throw LimitError(ErrorKind::Limit,
"memory output size overflow");
372 data_->resize(
static_cast<std::size_t
>(length));
373 position_ = std::min(position_, data_->size());
375 void flush()
override { requireOpen(); }
376 void sync()
override { requireOpen(); }
377 void close()
override { open_ =
false; }
380 void requireOpen()
const {
if (!open_) unsupported(
"access after close"); }
381 std::vector<std::uint8_t> *data_ =
nullptr;
382 std::size_t position_ = 0;
388std::uint64_t InputSource::tell()
const { unsupported(
"tell"); }
389void InputSource::seek(std::uint64_t) { unsupported(
"seek"); }
390void InputSource::reopen() { unsupported(
"reopen"); }
391std::uint64_t OutputSink::tell()
const { unsupported(
"tell"); }
392void OutputSink::seek(std::uint64_t) { unsupported(
"seek"); }
393void OutputSink::truncate(std::uint64_t) { unsupported(
"truncate"); }
394void OutputSink::sync() { unsupported(
"sync"); }
395void OutputSink::reopen() { unsupported(
"reopen"); }
397std::unique_ptr<InputSource> inputFromPath(
const std::filesystem::path &path) {
398 return std::make_unique<FileInput>(path);
400std::unique_ptr<InputSource> inputFromStdin() {
401 return std::make_unique<FileInput>(stdin,
false);
404std::unique_ptr<InputSource> inputFromFile(FILE *file,
bool takeOwnership) {
405 return std::make_unique<FileInput>(file, takeOwnership);
407std::unique_ptr<InputSource> inputFromStream(std::istream &stream) {
408 return std::make_unique<StreamInput>(stream);
410std::unique_ptr<InputSource> inputFromMemory(
const void *data, std::size_t size) {
411 return std::make_unique<MemoryInput>(data, size);
413std::unique_ptr<OutputSink> outputToPath(
const std::filesystem::path &path,
bool truncate) {
414 return std::make_unique<FileOutput>(path, truncate);
416std::unique_ptr<OutputSink> outputToStdout() {
417 return std::make_unique<FileOutput>(stdout,
false);
419std::unique_ptr<OutputSink> outputToFile(FILE *file,
bool takeOwnership) {
420 return std::make_unique<FileOutput>(file, takeOwnership);
422std::unique_ptr<OutputSink> outputToStream(std::ostream &stream) {
423 return std::make_unique<StreamOutput>(stream);
425std::unique_ptr<OutputSink> outputToMemory(std::vector<std::uint8_t> &data) {
426 return std::make_unique<MemoryOutput>(data);
write(SddsFile sdds_file, output_file)
Mostly backward compatible with the PyLHC sdds module write() function.
SddsFile read(input_file)
Mostly backward compatible with the PyLHC sdds module read() function.