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-2019 Made to Order Software Corporation
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 378 : BackBuffer::BackBuffer(std::istream & is, VirtualSeeker const & vs, ssize_t const chunk_size)
81 : : m_vs(vs)
82 : , m_chunk_size(chunk_size)
83 : , m_is(is)
84 397 : , m_file_pos(0)
85 : {
86 378 : if(!m_is)
87 : {
88 1 : throw InvalidException("BackBuffer() initialized with an invalid input stream.");
89 : }
90 377 : if(m_chunk_size <= 0)
91 : {
92 17 : throw InvalidException("Invalid chunk_size parameter, it has to be larger than zero.");
93 : }
94 :
95 360 : m_vs.vseekg(m_is, 0, std::ios::end);
96 360 : 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 360 : if(m_file_pos < 0)
103 : {
104 1 : throw IOException("Invalid virtual file endings.");
105 : }
106 359 : }
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 395 : ssize_t BackBuffer::readChunk(ssize_t& read_pointer)
142 : {
143 : // Update m_chunk_size and file position
144 395 : m_chunk_size = std::min<ssize_t>(static_cast<ssize_t>(m_file_pos), m_chunk_size);
145 395 : m_file_pos -= m_chunk_size;
146 395 : m_vs.vseekg(m_is, m_file_pos, std::ios::beg);
147 :
148 : // Make space for m_chunk_size new bytes
149 395 : insert(begin(), m_chunk_size, static_cast<char>(0));
150 :
151 : // Read in the next m_chunk_size bytes
152 395 : zipRead(m_is, *this, m_chunk_size);
153 395 : read_pointer += m_chunk_size;
154 :
155 395 : return m_is.good() ? m_chunk_size : 0;
156 : }
157 :
158 :
159 3 : } // 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
|