mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 01:40:04 +00:00 
			
		
		
		
	Initial QFileBuffer implementation
This commit is contained in:
		
							parent
							
								
									779d03c576
								
							
						
					
					
						commit
						08965aa7f3
					
				
					 3 changed files with 501 additions and 2 deletions
				
			
		|  | @ -160,12 +160,14 @@ set(HDR_UTIL source/scwx/qt/util/color.hpp | |||
|              source/scwx/qt/util/font_buffer.hpp | ||||
|              source/scwx/qt/util/json.hpp | ||||
|              source/scwx/qt/util/streams.hpp | ||||
|              source/scwx/qt/util/texture_atlas.hpp) | ||||
|              source/scwx/qt/util/texture_atlas.hpp | ||||
|              source/scwx/qt/util/q_file_buffer.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/texture_atlas.cpp | ||||
|              source/scwx/qt/util/q_file_buffer.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 | ||||
|  |  | |||
							
								
								
									
										306
									
								
								scwx-qt/source/scwx/qt/util/q_file_buffer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								scwx-qt/source/scwx/qt/util/q_file_buffer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,306 @@ | |||
| #include <scwx/qt/util/q_file_buffer.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <QFile> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace util | ||||
| { | ||||
| 
 | ||||
| static const std::string logPrefix_ = "scwx::qt::util::q_file_buffer"; | ||||
| static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||
| 
 | ||||
| // Adapted from Microsoft filebuf reference implementation
 | ||||
| // Copyright (c) Microsoft Corporation.
 | ||||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | ||||
| 
 | ||||
| class QFileBuffer::Impl | ||||
| { | ||||
| public: | ||||
|    explicit Impl(QFileBuffer* self) : self_ {self} {}; | ||||
|    ~Impl() = default; | ||||
| 
 | ||||
|    void ResetPutback(); | ||||
|    void SetPutback(); | ||||
| 
 | ||||
|    QFileBuffer* self_; | ||||
|    QFile        file_ {}; | ||||
|    char_type    putbackChar_ {}; | ||||
|    char_type*   putbackEback_ {nullptr}; | ||||
|    char_type*   putbackEgptr_ {nullptr}; | ||||
| }; | ||||
| 
 | ||||
| void QFileBuffer::Impl::ResetPutback() | ||||
| { | ||||
|    if (self_->eback() == &putbackChar_) | ||||
|    { | ||||
|       // Restore get buffer after putback
 | ||||
|       self_->setg(putbackEback_, putbackEback_, putbackEgptr_); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void QFileBuffer::Impl::SetPutback() | ||||
| { | ||||
|    if (self_->eback() != &putbackChar_) | ||||
|    { | ||||
|       // Save current get buffer
 | ||||
|       putbackEback_ = self_->eback(); | ||||
|       putbackEgptr_ = self_->egptr(); | ||||
|    } | ||||
|    self_->setg(&putbackChar_, &putbackChar_, &putbackChar_ + 1); | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::QFileBuffer() : std::streambuf(), p(std::make_unique<Impl>(this)) | ||||
| { | ||||
|    // Initialize read/write pointers
 | ||||
|    setg(0, 0, 0); | ||||
|    setp(0, 0); | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::QFileBuffer(const std::string&      filename, | ||||
|                          std::ios_base::openmode mode) : | ||||
|     QFileBuffer() | ||||
| { | ||||
|    open(filename, mode); | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::~QFileBuffer() = default; | ||||
| 
 | ||||
| QFileBuffer::QFileBuffer(QFileBuffer&&) noexcept            = default; | ||||
| QFileBuffer& QFileBuffer::operator=(QFileBuffer&&) noexcept = default; | ||||
| 
 | ||||
| bool QFileBuffer::is_open() const | ||||
| { | ||||
|    return p->file_.isOpen(); | ||||
| } | ||||
| 
 | ||||
| QFileBuffer* QFileBuffer::open(const std::string&      filename, | ||||
|                                std::ios_base::openmode mode) | ||||
| { | ||||
|    // If the associated file is already open, return a null pointer right away
 | ||||
|    if (is_open()) | ||||
|    { | ||||
|       return nullptr; | ||||
|    } | ||||
| 
 | ||||
|    // Validate supported modes
 | ||||
|    if (mode & std::ios_base::out || mode & std::ios_base::app || | ||||
|        mode & std::ios_base::trunc) | ||||
|    { | ||||
|       logger_->error("open(): write mode not supported"); | ||||
|       return nullptr; | ||||
|    } | ||||
| 
 | ||||
|    // Convert std iostream flags to Qt flags
 | ||||
|    QIODeviceBase::OpenMode flags {}; | ||||
|    if (mode & std::ios_base::in) | ||||
|    { | ||||
|       flags |= QIODeviceBase::OpenModeFlag::ReadOnly; | ||||
|    } | ||||
|    if ((mode & std::ios_base::binary) != std::ios_base::binary) | ||||
|    { | ||||
|       flags |= QIODeviceBase::OpenModeFlag::Text; | ||||
|    } | ||||
| 
 | ||||
|    // Set the filename and open the file
 | ||||
|    p->file_.setFileName(QString::fromStdString(filename)); | ||||
|    bool isOpen = p->file_.open(flags); | ||||
| 
 | ||||
|    if (isOpen) | ||||
|    { | ||||
|       // Seek to end if requested
 | ||||
|       if (mode & std::ios_base::ate) | ||||
|       { | ||||
|          // Seek to end
 | ||||
|          p->file_.seek(p->file_.size()); | ||||
|       } | ||||
| 
 | ||||
|       // Initialize read/write pointers
 | ||||
|       setg(0, 0, 0); | ||||
|       setp(0, 0); | ||||
|    } | ||||
| 
 | ||||
|    return isOpen ? this : nullptr; | ||||
| } | ||||
| 
 | ||||
| QFileBuffer* QFileBuffer::close() | ||||
| { | ||||
|    // If the associated file is already closed, return a null pointer right away
 | ||||
|    if (!p->file_.isOpen()) | ||||
|    { | ||||
|       return nullptr; | ||||
|    } | ||||
| 
 | ||||
|    // Close the file
 | ||||
|    p->file_.close(); | ||||
| 
 | ||||
|    return this; | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::int_type QFileBuffer::pbackfail(int_type c) | ||||
| { | ||||
|    if (gptr() && eback() < gptr() && | ||||
|        (traits_type::eq_int_type(traits_type::eof(), c) || | ||||
|         traits_type::eq_int_type(traits_type::to_int_type(gptr()[-1]), c))) | ||||
|    { | ||||
|       // Just back up position
 | ||||
|       gbump(static_cast<int>(sizeof(char_type)) * -1); | ||||
|       return traits_type::not_eof(c); | ||||
|    } | ||||
|    else if (!is_open() || traits_type::eq_int_type(traits_type::eof(), c)) | ||||
|    { | ||||
|       // No open QFile or EOF, fail
 | ||||
|       return traits_type::eof(); | ||||
|    } | ||||
|    else if (gptr() != &p->putbackChar_) | ||||
|    { | ||||
|       // Put back to buffer
 | ||||
|       p->putbackChar_ = traits_type::to_char_type(c); | ||||
|       p->SetPutback(); | ||||
|       return c; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       // Nowhere to put back
 | ||||
|       return traits_type::eof(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::pos_type QFileBuffer::seekoff(off_type               off, | ||||
|                                            std::ios_base::seekdir dir, | ||||
|                                            std::ios_base::openmode /* which */) | ||||
| { | ||||
|    pos_type newPos {pos_type(off_type(-1))}; | ||||
| 
 | ||||
|    switch (dir) | ||||
|    { | ||||
|    case std::ios_base::beg: | ||||
|       // Seek using absolute position
 | ||||
|       newPos = seekpos(off, std::ios_base::in); | ||||
|       break; | ||||
| 
 | ||||
|    case std::ios_base::cur: | ||||
|    { | ||||
|       const pos_type currentPos {p->file_.pos() - (egptr() - gptr())}; | ||||
|       pos_type       updatePos {currentPos + off}; | ||||
| 
 | ||||
|       // If the putback buffer is not empty, decrement the offset
 | ||||
|       if (gptr() == &p->putbackChar_) | ||||
|       { | ||||
|          updatePos -= static_cast<off_type>(sizeof(char_type)); | ||||
|       } | ||||
| 
 | ||||
|       // Seek the file
 | ||||
|       if (p->file_.seek(updatePos)) | ||||
|       { | ||||
|          // Record updated position
 | ||||
|          newPos = updatePos; | ||||
|       } | ||||
| 
 | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    case std::ios_base::end: | ||||
|    { | ||||
|       const pos_type endPos {p->file_.size()}; | ||||
|       const pos_type updatePos {endPos + off}; | ||||
| 
 | ||||
|       // Seek the file
 | ||||
|       if (p->file_.seek(updatePos)) | ||||
|       { | ||||
|          // Record updated position
 | ||||
|          newPos = updatePos; | ||||
|       } | ||||
| 
 | ||||
|       break; | ||||
|    } | ||||
|    } | ||||
| 
 | ||||
|    if (newPos != static_cast<off_type>(-1)) | ||||
|    { | ||||
|       p->ResetPutback(); | ||||
|    } | ||||
| 
 | ||||
|    return newPos; | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::pos_type QFileBuffer::seekpos(pos_type pos, | ||||
|                                            std::ios_base::openmode /* which */) | ||||
| { | ||||
|    pos_type newPos {pos_type(off_type(-1))}; | ||||
| 
 | ||||
|    // Seek the file
 | ||||
|    if (p->file_.seek(pos)) | ||||
|    { | ||||
|       // Record updated position
 | ||||
|       newPos = pos; | ||||
| 
 | ||||
|       p->ResetPutback(); | ||||
|    } | ||||
| 
 | ||||
|    return newPos; | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::int_type QFileBuffer::underflow() | ||||
| { | ||||
|    // This function is only called if gptr() == nullptr or gptr() > egptr()
 | ||||
|    // (i.e., all buffer data has been read)
 | ||||
|    int_type c; | ||||
| 
 | ||||
|    if (gptr() && gptr() < egptr()) | ||||
|    { | ||||
|       // Return buffered
 | ||||
|       return traits_type::to_int_type(*gptr()); | ||||
|    } | ||||
|    else if (traits_type::eq_int_type(traits_type::eof(), c = uflow())) | ||||
|    { | ||||
|       // uflow failed, return EOF
 | ||||
|       return c; | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       // Get a character, don't point past it
 | ||||
|       pbackfail(c); | ||||
|       return c; | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| QFileBuffer::int_type QFileBuffer::uflow() | ||||
| { | ||||
|    if (gptr() && gptr() < egptr()) | ||||
|    { | ||||
|       // Return buffered
 | ||||
|       int_type c = traits_type::to_int_type(*gptr()); | ||||
|       gbump(sizeof(char_type)); | ||||
|       return c; | ||||
|    } | ||||
| 
 | ||||
|    if (!p->file_.isOpen()) | ||||
|    { | ||||
|       // No open QFile, fail
 | ||||
|       return traits_type::eof(); | ||||
|    } | ||||
| 
 | ||||
|    p->ResetPutback(); | ||||
| 
 | ||||
|    // Read the next character
 | ||||
|    char_type c; | ||||
|    return p->file_.read(reinterpret_cast<char*>(&c), sizeof(char_type)) > 0 ? | ||||
|              traits_type::to_int_type(c) : | ||||
|              traits_type::eof(); | ||||
| } | ||||
| 
 | ||||
| std::streamsize QFileBuffer::xsgetn(char_type* s, std::streamsize count) | ||||
| { | ||||
|    // Read up to count bytes, forwarding the return value from QFile::read
 | ||||
|    // (return negative values as zero)
 | ||||
|    return std::max<std::streamsize>( | ||||
|       p->file_.read(reinterpret_cast<char*>(s), count * sizeof(char_type)), 0); | ||||
| } | ||||
| 
 | ||||
| } // namespace util
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										191
									
								
								scwx-qt/source/scwx/qt/util/q_file_buffer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								scwx-qt/source/scwx/qt/util/q_file_buffer.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,191 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <streambuf> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace util | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * The QFileBuffer class is a std::streambuf interface to a QFile, allowing use | ||||
|  * with C++ stream-based I/O. | ||||
|  * | ||||
|  * QFileBuffer has read-only support. Locales are ignored, and no conversions | ||||
|  * are performed. | ||||
|  * | ||||
|  * Documentation for functions derived from | ||||
|  * https://en.cppreference.com/ SPDX-License-Identifier: CC BY-SA 3.0
 | ||||
|  */ | ||||
| class QFileBuffer : public std::streambuf | ||||
| { | ||||
| public: | ||||
|    /**
 | ||||
|     * Constructs a new QFileBuffer object. The created object is not associated | ||||
|     * with a file, and is_open() returns false. | ||||
|     */ | ||||
|    explicit QFileBuffer(); | ||||
| 
 | ||||
|    /**
 | ||||
|     * Constructs a new QFileBuffer object, then associated the object with a | ||||
|     * file by calling open(filename, mode). If the open call is successful, | ||||
|     * is_open() returns true. | ||||
|     */ | ||||
|    explicit QFileBuffer(const std::string&      filename, | ||||
|                         std::ios_base::openmode mode = std::ios_base::in); | ||||
|    ~QFileBuffer(); | ||||
| 
 | ||||
|    QFileBuffer(const QFileBuffer&)            = delete; | ||||
|    QFileBuffer& operator=(const QFileBuffer&) = delete; | ||||
| 
 | ||||
|    QFileBuffer(QFileBuffer&&) noexcept; | ||||
|    QFileBuffer& operator=(QFileBuffer&&) noexcept; | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Checks if the associated file is open | ||||
|     * | ||||
|     * Returns true if the most recent call to open() succeeded and there has | ||||
|     * been no call to close() since then. | ||||
|     * | ||||
|     * @return true if the associated file is open, false otherwise | ||||
|     */ | ||||
|    bool is_open() const; | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Opens a file and configures it as the associated character sequence | ||||
|     * | ||||
|     * Opens the file with the given name. If the associated file was already | ||||
|     * open, returns a null pointer right away. | ||||
|     * | ||||
|     * @param filename The file name to open | ||||
|     * @param openmode The file opening mode, a binary OR of the std::ios_base | ||||
|     * modes | ||||
|     * | ||||
|     * @return this on success, a null pointer on failure | ||||
|     */ | ||||
|    QFileBuffer* open(const std::string&      filename, | ||||
|                      std::ios_base::openmode mode = std::ios_base::in); | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Flushes the put area buffer and closes the associated file | ||||
|     * | ||||
|     * Closes the file, regardless of whether any of the preceding calls | ||||
|     * succeeded or failed. | ||||
|     * | ||||
|     * If any of the function calls made fails, returns a null pointer. If any of | ||||
|     * the function calls made throws an exception, the exception is caught and | ||||
|     * rethrown after closing. If the file is already closed, returns a null | ||||
|     * pointer right away. | ||||
|     * | ||||
|     * @return this on success, a null pointer on failure | ||||
|     */ | ||||
|    QFileBuffer* close(); | ||||
| 
 | ||||
| protected: | ||||
|    /**
 | ||||
|     * @brief Backs out the input sequence to unget a character, not affecting | ||||
|     * the associated file | ||||
|     * | ||||
|     * @param c The character to put back, or Traits::eof() to indicate that | ||||
|     * backing up of the get area is requested | ||||
|     * | ||||
|     * @return c on success except if c was Traits::eof(), in which case | ||||
|     * Traits::not_eof(c) is returned. Traits::eof() on failure. | ||||
|     */ | ||||
|    virtual int_type pbackfail(int_type c = traits_type::eof()) override; | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Repositions the file position, using relative addressing | ||||
|     * | ||||
|     * Sets the position indicator of the input and/or output sequence relative | ||||
|     * to some other position. | ||||
|     * | ||||
|     * @param off Relative position to set the position indicator to | ||||
|     * @param dir Defines base position to apply the relative offset to | ||||
|     * @param which Defines which of the input and/or output sequences to affect | ||||
|     * | ||||
|     * @return The resulting absolute position as defined by the position | ||||
|     * indicator. | ||||
|     */ | ||||
|    virtual pos_type | ||||
|    seekoff(off_type                off, | ||||
|            std::ios_base::seekdir  dir, | ||||
|            std::ios_base::openmode which = std::ios_base::in | | ||||
|                                            std::ios_base::out) override; | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Repositions the file position, using absolute addressing | ||||
|     * | ||||
|     * Sets the position indicator of the input and/or output sequence to an | ||||
|     * absolute position. | ||||
|     * | ||||
|     * @param pos Absolute position to set the position indicator to | ||||
|     * @param which Defines which of the input and/or output sequences to affect | ||||
|     * | ||||
|     * @return The resulting absolute position as defined by the position | ||||
|     * indicator. | ||||
|     */ | ||||
|    virtual pos_type | ||||
|    seekpos(pos_type                pos, | ||||
|            std::ios_base::openmode which = std::ios_base::in | | ||||
|                                            std::ios_base::out) override; | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Reads from the associated file | ||||
|     * | ||||
|     * Ensures that at least one character is available in the input area by | ||||
|     * updating the pointers to the input area (if needed) and reading more data | ||||
|     * in from the input sequence (if applicable). Returns the value of that | ||||
|     * character (converted to int_type with Traits::to_int_type(c)) on success | ||||
|     * or Traits::eof() on failure. | ||||
|     * | ||||
|     * The function may update gptr, egptr and eback pointers to define the | ||||
|     * location of newly loaded data (if any). On failure, the function ensures | ||||
|     * that either gptr() == nullptr or gptr() == egptr. | ||||
|     * | ||||
|     * @return The value of the character pointed to by the get pointer after the | ||||
|     * call on success, or Traits::eof() otherwise. | ||||
|     */ | ||||
|    virtual int_type underflow() override; | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Reads from the associated file and advances the next pointer in the | ||||
|     * get area | ||||
|     * | ||||
|     * Behaves like the underflow(), except that if underflow() succeeds (does | ||||
|     * not return Traits::eof()), then advances the next pointer for the get | ||||
|     * area. In orther words, consumes one of the characters obtained by | ||||
|     * underflow(). | ||||
|     * | ||||
|     * @return The value of the character that was read and consumed in case of | ||||
|     * success, or Traits::eof() in case of failure. | ||||
|     */ | ||||
|    virtual int_type uflow() override; | ||||
| 
 | ||||
|    /**
 | ||||
|     * @brief Reads multiple characters from the input sequence | ||||
|     * | ||||
|     * Reads count characters from the input sequence and stores them into a | ||||
|     * character array pointed to by s. The characters are read as if by repeated | ||||
|     * calls to sbumpc(). That is, if less than count characters are immediately | ||||
|     * available, the function calls uflow() to provide more until Traits::eof() | ||||
|     * is returned. | ||||
|     * | ||||
|     * @param s Pointer to the beginning of a char_type array | ||||
|     * @param count Maximum number of characters to read | ||||
|     * | ||||
|     * @return The number of characters successfully read. If it is less than | ||||
|     * count the input sequence has reached the end. | ||||
|     */ | ||||
|    virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override; | ||||
| 
 | ||||
| private: | ||||
|    class Impl; | ||||
|    std::unique_ptr<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace util
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat