diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 540124f7..dd855214 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -161,13 +161,15 @@ set(HDR_UTIL source/scwx/qt/util/color.hpp source/scwx/qt/util/json.hpp source/scwx/qt/util/streams.hpp source/scwx/qt/util/texture_atlas.hpp - source/scwx/qt/util/q_file_buffer.hpp) + source/scwx/qt/util/q_file_buffer.hpp + source/scwx/qt/util/q_file_input_stream.hpp) set(SRC_UTIL source/scwx/qt/util/color.cpp source/scwx/qt/util/font.cpp source/scwx/qt/util/font_buffer.cpp source/scwx/qt/util/json.cpp source/scwx/qt/util/texture_atlas.cpp - source/scwx/qt/util/q_file_buffer.cpp) + source/scwx/qt/util/q_file_buffer.cpp + source/scwx/qt/util/q_file_input_stream.cpp) set(HDR_VIEW source/scwx/qt/view/level2_product_view.hpp source/scwx/qt/view/level3_product_view.hpp source/scwx/qt/view/level3_radial_view.hpp diff --git a/scwx-qt/source/scwx/qt/util/q_file_buffer.cpp b/scwx-qt/source/scwx/qt/util/q_file_buffer.cpp index 28640717..5d55361c 100644 --- a/scwx-qt/source/scwx/qt/util/q_file_buffer.cpp +++ b/scwx-qt/source/scwx/qt/util/q_file_buffer.cpp @@ -72,6 +72,13 @@ QFileBuffer::~QFileBuffer() = default; QFileBuffer::QFileBuffer(QFileBuffer&&) noexcept = default; QFileBuffer& QFileBuffer::operator=(QFileBuffer&&) noexcept = default; +void QFileBuffer::swap(QFileBuffer& other) +{ + // Swap the base class and managed implementation pointer + std::streambuf::swap(other); + p.swap(other.p); +} + bool QFileBuffer::is_open() const { return p->file_.isOpen(); @@ -147,7 +154,7 @@ QFileBuffer::int_type QFileBuffer::pbackfail(int_type c) traits_type::eq_int_type(traits_type::to_int_type(gptr()[-1]), c))) { // Just back up position - gbump(static_cast(sizeof(char_type)) * -1); + gbump(-1); return traits_type::not_eof(c); } else if (!is_open() || traits_type::eq_int_type(traits_type::eof(), c)) @@ -274,7 +281,7 @@ QFileBuffer::int_type QFileBuffer::uflow() { // Return buffered int_type c = traits_type::to_int_type(*gptr()); - gbump(sizeof(char_type)); + gbump(1); return c; } diff --git a/scwx-qt/source/scwx/qt/util/q_file_buffer.hpp b/scwx-qt/source/scwx/qt/util/q_file_buffer.hpp index cb63ecd2..eaccb403 100644 --- a/scwx-qt/source/scwx/qt/util/q_file_buffer.hpp +++ b/scwx-qt/source/scwx/qt/util/q_file_buffer.hpp @@ -29,7 +29,7 @@ public: explicit QFileBuffer(); /** - * Constructs a new QFileBuffer object, then associated the object with a + * Constructs a new QFileBuffer object, then associates the object with a * file by calling open(filename, mode). If the open call is successful, * is_open() returns true. */ @@ -43,6 +43,13 @@ public: QFileBuffer(QFileBuffer&&) noexcept; QFileBuffer& operator=(QFileBuffer&&) noexcept; + /** + * @brief Swaps two QFileBuffer objects + * + * Exchanges the contents of the QFile buffer with those of the other. + */ + void swap(QFileBuffer& other); + /** * @brief Checks if the associated file is open * diff --git a/scwx-qt/source/scwx/qt/util/q_file_input_stream.cpp b/scwx-qt/source/scwx/qt/util/q_file_input_stream.cpp new file mode 100644 index 00000000..04fddb4a --- /dev/null +++ b/scwx-qt/source/scwx/qt/util/q_file_input_stream.cpp @@ -0,0 +1,96 @@ +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace util +{ + +static const std::string logPrefix_ = "scwx::qt::util::q_file_input_stream"; + +// Adapted from Microsoft ifstream reference implementation +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +class QFileInputStream::Impl +{ +public: + explicit Impl() : buffer_ {} {}; + explicit Impl(const std::string& filename, std::ios_base::openmode mode) : + buffer_ {filename, mode} {}; + ~Impl() = default; + + QFileBuffer buffer_; +}; + +QFileInputStream::QFileInputStream() : + std::istream(nullptr), p(std::make_unique()) +{ + std::basic_ios::rdbuf(&p->buffer_); +} + +QFileInputStream::QFileInputStream(const std::string& filename, + std::ios_base::openmode mode) : + std::istream(nullptr), + p(std::make_unique(filename, mode | std::ios_base::in)) +{ + std::basic_ios::rdbuf(&p->buffer_); + if (!p->buffer_.is_open()) + { + setstate(std::ios_base::failbit); + } +} + +QFileInputStream::~QFileInputStream() = default; + +QFileInputStream::QFileInputStream(QFileInputStream&& other) noexcept : + std::istream(nullptr), p(std::make_unique()) +{ + swap(other); +}; +QFileInputStream& +QFileInputStream::operator=(QFileInputStream&&) noexcept = default; + +void QFileInputStream::swap(QFileInputStream& other) +{ + // Swap the base class and managed implementation pointer + std::istream::swap(other); + p.swap(other.p); +} + +bool QFileInputStream::is_open() const +{ + return p->buffer_.is_open(); +} + +void QFileInputStream::open(const std::string& filename, + std::ios_base::openmode mode) +{ + if (p->buffer_.open(filename, mode | std::ios_base::in) == nullptr) + { + setstate(std::ios_base::failbit); + } + else + { + clear(); + } +} + +void QFileInputStream::close() +{ + if (p->buffer_.close() == nullptr) + { + setstate(std::ios_base::failbit); + } +} + +QFileBuffer* QFileInputStream::rdbuf() const +{ + return &p->buffer_; +} + +} // namespace util +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/util/q_file_input_stream.hpp b/scwx-qt/source/scwx/qt/util/q_file_input_stream.hpp new file mode 100644 index 00000000..c28372ad --- /dev/null +++ b/scwx-qt/source/scwx/qt/util/q_file_input_stream.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include + +namespace scwx +{ +namespace qt +{ +namespace util +{ + +class QFileBuffer; + +/** + * The QFileInputStream class is a std::istream interface to a QFile, allowing + * use with C++ stream-based I/O. + * + * Documentation for functions derived from + * https://en.cppreference.com/ SPDX-License-Identifier: CC BY-SA 3.0 + */ +class QFileInputStream : public std::istream +{ +public: + /** + * Constructs a new QFileInputStream object. The created object is not + * associated with a file: default-constructs the QFileBuffer and constructs + * the base with the pointer to this default-constructed QFileBuffer member. + */ + explicit QFileInputStream(); + + /** + * Constructs a new QFileInputStream object, then associates the object with + * a file by calling rdbuf()->open(filename, mode | std::ios_base::in). If + * the open() call returns a null pointer, sets setstate(failbit). + */ + explicit QFileInputStream(const std::string& filename, + std::ios_base::openmode mode = std::ios_base::in); + ~QFileInputStream(); + + QFileInputStream(const QFileInputStream&) = delete; + QFileInputStream& operator=(const QFileInputStream&) = delete; + + QFileInputStream(QFileInputStream&&) noexcept; + QFileInputStream& operator=(QFileInputStream&&) noexcept; + + /** + * @brief Swaps two QFileInputStream objects + * + * Exchanges the contents of the QFile input stream with those of the other. + */ + void swap(QFileInputStream& other); + + /** + * @brief Checks if the stream has an associated file + * + * Checks if the file stream has an associated file. Effectively calls + * rdbuf()->is_open(). + * + * @return true if the associated file is open, false otherwise + */ + bool is_open() const; + + /** + * @brief Opens a file and associates it with the stream + * + * Opens and associates the file with name filename with the file stream. + * Effectively calls rdbuf()->open(filename, mode | ios_base::in). Calls + * setstate(failbit) on failure. + * + * @param filename The file name to open + * @param openmode The file opening mode, a binary OR of the std::ios_base + * modes + */ + void open(const std::string& filename, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * @brief Close the associated file + * + * Closes the associated file. Effectively calls rdbuf()->close(). If an + * error occurs during operation, setstate(failbit) is called. + */ + void close(); + + /** + * @brief Returns the underlying raw file device object + * + * Returns a pointer to the underlying raw file device object. + * + * @return Pointer to the underlying raw file device + */ + QFileBuffer* rdbuf() const; + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace util +} // namespace qt +} // namespace scwx