Line data Source code
1 : /* 2 : Zipios -- a small C++ library that provides easy access to .zip files. 3 : 4 : Copyright (C) 2000-2007 Thomas Sondergaard 5 : Copyright (c) 2015-2022 Made to Order Software Corp. All Rights Reserved 6 : 7 : This library is free software; you can redistribute it and/or 8 : modify it under the terms of the GNU Lesser General Public 9 : License as published by the Free Software Foundation; either 10 : version 2.1 of the License, or (at your option) any later version. 11 : 12 : This library is distributed in the hope that it will be useful, 13 : but WITHOUT ANY WARRANTY; without even the implied warranty of 14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 : Lesser General Public License for more details. 16 : 17 : You should have received a copy of the GNU Lesser General Public 18 : License along with this library; if not, write to the Free Software 19 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 : */ 21 : 22 : /** \file 23 : * \brief The implementation file of zipios::BackBuffer. 24 : * 25 : * This class implements the functions necessary to read a file 26 : * backward. 27 : */ 28 : 29 : #include "backbuffer.hpp" 30 : 31 : #include "zipios/zipiosexceptions.hpp" 32 : 33 : #include <algorithm> 34 : 35 : namespace zipios 36 : { 37 : 38 : /** \class BackBuffer 39 : * \brief To read a file by chunk from the end. 40 : * 41 : * A BackBuffer instance is useful for reading the last part of a 42 : * file in an efficient manner, when it is not known exactly how far 43 : * back (towards the front!) to go, to find the start of the desired 44 : * data block. 45 : * 46 : * BackBuffer is an std::vector<unsigned char> that fills 47 : * itself with data from a file by reading chunks from the end of the 48 : * file progressing towards the start. Upon construction the 49 : * BackBuffer instance is associated with a file and a chunksize can 50 : * be specified. To read a chunk of the file into the BackBuffer call 51 : * readChunk(). 52 : * 53 : * Note that this is not a good option for really large files as the 54 : * buffer is enlarged using an insert() on each call to readChunk(). 55 : */ 56 : 57 : 58 : 59 : /** BackBuffer constructor. 60 : * 61 : * Initialize a buffer that reads data backward. 62 : * 63 : * The content of the buffer is defined as the content of the \p is 64 : * stream. The stream must be open and seekable. The file pointer 65 : * of the stream is modified by this constructor and on each 66 : * call to readChunk(). 67 : * 68 : * \exception IOException 69 : * This exception is raised when the VirtualSeeker (\p vs) returns 70 : * an invalid answer determining the size of the stream. 71 : * 72 : * \param[in,out] is The istream to read the data from. The stream must 73 : * be seekable, as BackBuffer will reposition the 74 : * file pointer to read chunks from the back of the 75 : * file. 76 : * \param[in] vs The virtual seeker used to change the file pointer. 77 : * \param[in] chunk_size Specifies the size of the chunks to read the 78 : * file into the BackBuffer in. 79 : */ 80 390 : BackBuffer::BackBuffer(std::istream & is, VirtualSeeker const & vs, ssize_t const chunk_size) 81 390 : : m_vs(vs) 82 390 : , m_chunk_size(chunk_size) 83 390 : , m_is(is) 84 390 : , m_file_pos(0) 85 : { 86 390 : if(!m_is) 87 : { 88 1 : throw InvalidException("BackBuffer() initialized with an invalid input stream."); 89 : } 90 389 : if(m_chunk_size <= 0) 91 : { 92 17 : throw InvalidException("Invalid chunk_size parameter, it has to be larger than zero."); 93 : } 94 : 95 372 : m_vs.vseekg(m_is, 0, std::ios::end); 96 372 : m_file_pos = m_vs.vtellg(m_is); 97 : 98 : // The following should only happens when m_vs.startOffset() is a 99 : // position in the file that lies after m_vs.endOffset(), which 100 : // is clearly not a valid situation. However, vtellg() may just 101 : // fail too. 102 372 : if(m_file_pos < 0) 103 : { 104 1 : throw IOException("Invalid virtual file endings."); 105 : } 106 390 : } 107 : 108 : 109 : /** \brief Read a chunk of data. 110 : * 111 : * Reads another chunk and returns the size of the chunk that has 112 : * been read. Returns 0 on I/O failure. 113 : * 114 : * When a new chunk is read in the already stored bytes change 115 : * position in the BackBuffer. \p read_pointer is assumed by 116 : * readChunk() to be a pointer into a position in the BackBuffer, 117 : * and is updated to point to the same position in the file 118 : * as it pointed to before the new chunk was prepended. 119 : * 120 : * When first calling readChunk(), \p read_pointer is expected 121 : * to be zero and represent the position of EOF. 122 : * 123 : * \warning 124 : * The file is read backward. 125 : * 126 : * \warning 127 : * The function may change m_chunk_size. In the end will be 128 : * zero and no more data can be read. 129 : * 130 : * \warning 131 : * The function changes the file pointer of the attached stream. 132 : * 133 : * \bug 134 : * We may want to throw an error if m_is.is_good() returns false. 135 : * 136 : * \param[in,out] read_pointer The buffer pointer. 137 : * 138 : * \return The number of bytes read if any, otherwise zero. Note that if 139 : * an error occurs, the function also returns zero. 140 : */ 141 407 : ssize_t BackBuffer::readChunk(ssize_t & read_pointer) 142 : { 143 : // Update m_chunk_size and file position 144 407 : m_chunk_size = std::min<ssize_t>(static_cast<ssize_t>(m_file_pos), m_chunk_size); 145 407 : m_file_pos -= m_chunk_size; 146 407 : m_vs.vseekg(m_is, m_file_pos, std::ios::beg); 147 : 148 : // Make space for m_chunk_size new bytes 149 407 : insert(begin(), m_chunk_size, static_cast<char>(0)); 150 : 151 : // Read in the next m_chunk_size bytes 152 407 : zipRead(m_is, *this, m_chunk_size); 153 407 : read_pointer += m_chunk_size; 154 : 155 407 : return m_is.good() ? m_chunk_size : 0; 156 : } 157 : 158 : 159 : } // zipios namespace 160 : // Local Variables: 161 : // mode: cpp 162 : // indent-tabs-mode: nil 163 : // c-basic-offset: 4 164 : // tab-width: 4 165 : // End: 166 : 167 : // vim: ts=4 sw=4 et