//***************************************************************************/ // This software is released under the 2-Clause BSD license, included // below. // // Copyright (c) 2019, Aous Naman // Copyright (c) 2019, Kakadu Software Pty Ltd, Australia // Copyright (c) 2019, The University of New South Wales, Australia // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //***************************************************************************/ // This file is part of the OpenJPH software implementation. // File: ojph_file.h // Author: Aous Naman // Date: 28 August 2019 //***************************************************************************/ #ifndef OJPH_FILE_H #define OJPH_FILE_H #include #include #include "ojph_arch.h" namespace ojph { //////////////////////////////////////////////////////////////////////////// #ifdef OJPH_OS_WINDOWS int inline ojph_fseek(FILE* stream, si64 offset, int origin) { return _fseeki64(stream, offset, origin); } si64 inline ojph_ftell(FILE* stream) { return _ftelli64(stream); } #else int inline ojph_fseek(FILE* stream, si64 offset, int origin) { return fseeko(stream, offset, origin); } si64 inline ojph_ftell(FILE* stream) { return ftello(stream); } #endif //////////////////////////////////////////////////////////////////////////// class OJPH_EXPORT outfile_base { public: public: enum seek : int { OJPH_SEEK_SET = SEEK_SET, OJPH_SEEK_CUR = SEEK_CUR, OJPH_SEEK_END = SEEK_END }; virtual ~outfile_base() {} virtual size_t write(const void *ptr, size_t size) = 0; virtual si64 tell() { return 0; } virtual int seek(si64 offset, enum outfile_base::seek origin) { ojph_unused(offset); ojph_unused(origin); return -1; /* always fail, to remind you to write an implementation */ } virtual void flush() {} virtual void close() {} }; //////////////////////////////////////////////////////////////////////////// class OJPH_EXPORT j2c_outfile : public outfile_base { public: j2c_outfile() { fh = 0; } ~j2c_outfile() override { if (fh) fclose(fh); } void open(const char *filename); size_t write(const void *ptr, size_t size) override; si64 tell() override; void flush() override; void close() override; private: FILE *fh; }; //*************************************************************************/ /** @brief mem_outfile stores encoded j2k codestreams in memory * * This code was first developed by Chris Hafey https://github.com/chafey * I took the code and integrated with OpenJPH, with some modifications. * * This class serves as a memory-based file storage. * For example, generated j2k codestream is stored in memory * instead of a conventional file. The memory buffer associated with * this class grows with the addition of new data. * * memory data can be accessed using get_data() */ class OJPH_EXPORT mem_outfile : public outfile_base { public: /** A constructor */ mem_outfile(); /** A destructor */ ~mem_outfile() override; mem_outfile(mem_outfile const&) = delete; mem_outfile& operator=(mem_outfile const&) = delete; /** * Move construction leaves the moved-from value in default constructed state * and transfers ownership of the internal state to the moved-to instance. **/ mem_outfile(mem_outfile &&) noexcept; /** * move assignment with the same ownership transfer semantics as * move construction. **/ mem_outfile& operator=(mem_outfile&&) noexcept; /** * @brief Call this function to open a memory file. * * This function creates a memory buffer to be used for storing * the generated j2k codestream. * * @param initial_size is the initial memory buffer size. * The default value is 2^16. * @param clear_mem if set to true, all allocated memory is reset to 0 */ void open(size_t initial_size = 65536, bool clear_mem = false); /** * @brief Call this function to write data to the memory file. * * This function adds new data to the memory file. The memory buffer * of the file grows as needed. * * @param ptr is a pointer to new data. * @param size the number of bytes in the new data. */ size_t write(const void *ptr, size_t size) override; /** * @brief Call this function to know the file size (i.e., number of * bytes used to store the file). * * @return the file size. */ si64 tell() override { return cur_ptr - buf; } /** * @brief Call this function to change write pointer location; the * function can expand file storage. * * @return 0 on success, non-zero otherwise (not used). */ int seek(si64 offset, enum outfile_base::seek origin) override; /** Call this function to close the file and deallocate memory * * The object can be used again after calling close */ void close() override; /** * @brief Call this function to access memory file data. * * It is not recommended to store the returned value because buffer * storage address can change between write calls. * * @return a constant pointer to the data. */ const ui8* get_data() { return buf; } /** * @brief Call this function to access memory file data (for const * objects) * * This is similar to the above function, except that it can be used * with constant objects. * * @return a constant pointer to the data. */ const ui8* get_data() const { return buf; } /** * @brief Call this function to write the memory file data to a file * */ void write_to_file(const char *file_name) const; /** * @brief Call this function to get the used size of the memory file. * * @return the used size of the memory file in bytes. */ size_t get_used_size() const { return used_size; } /** * @brief Call this function to get the total buffer size of the memory * file including unused space (this is the allocated memory). * * @return the full size of the memory file in bytes. */ size_t get_buf_size() const { return buf_size; } private: /** * @brief A utility function to swap the contents of two instances */ void swap(mem_outfile& other) noexcept; /** * @brief This function expands storage by x1.5 needed space. * * It sets cur_ptr correctly, and clears the extended area of the * buffer. It optionally clear the whole buffer * * @param new_size New size of the buffer * @param clear_all Set to true to clear whole buffer, not just expansion */ void expand_storage(size_t new_size, bool clear_all); private: bool is_open; bool clear_mem; size_t buf_size; size_t used_size; ui8 *buf; ui8 *cur_ptr; private: static const size_t ALIGNED_ALLOC_MASK = 4096 - 1; }; //////////////////////////////////////////////////////////////////////////// class OJPH_EXPORT infile_base { public: enum seek : int { OJPH_SEEK_SET = SEEK_SET, OJPH_SEEK_CUR = SEEK_CUR, OJPH_SEEK_END = SEEK_END }; virtual ~infile_base() {} //read reads size bytes, returns the number of bytes read virtual size_t read(void *ptr, size_t size) = 0; //seek returns 0 on success virtual int seek(si64 offset, enum infile_base::seek origin) = 0; virtual si64 tell() = 0; virtual bool eof() = 0; virtual void close() {} }; //////////////////////////////////////////////////////////////////////////// class OJPH_EXPORT j2c_infile : public infile_base { public: j2c_infile() { fh = 0; } ~j2c_infile() override { if (fh) fclose(fh); } void open(const char *filename); //read reads size bytes, returns the number of bytes read size_t read(void *ptr, size_t size) override; //seek returns 0 on success int seek(si64 offset, enum infile_base::seek origin) override; si64 tell() override; bool eof() override { return feof(fh) != 0; } void close() override; private: FILE *fh; }; //////////////////////////////////////////////////////////////////////////// class OJPH_EXPORT mem_infile : public infile_base { public: mem_infile() { close(); } ~mem_infile() override { } mem_infile(mem_infile const&) = delete; mem_infile& operator=(mem_infile const&) = delete; /** * Move construction leaves the moved-from value in default constructed state * and transfers ownership of the internal state to the moved-to instance. **/ mem_infile(mem_infile &&) noexcept; /** * move assignment with the same ownership transfer semantics as * move construction. **/ mem_infile& operator=(mem_infile&&) noexcept; void open(const ui8* data, size_t size); //read reads size bytes, returns the number of bytes read size_t read(void *ptr, size_t size) override; //seek returns 0 on success int seek(si64 offset, enum infile_base::seek origin) override; si64 tell() override { return cur_ptr - data; } bool eof() override { return cur_ptr >= data + size; } void close() override { data = cur_ptr = NULL; size = 0; } private: // swap the contents of two instances void swap(mem_infile&) noexcept; const ui8 *data, *cur_ptr; size_t size; }; } #endif // !OJPH_FILE_H