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 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 :
34 : namespace zipios
35 : {
36 :
37 : /** \class BackBuffer
38 : * \brief To read a file by chunk from the end.
39 : *
40 : * A BackBuffer instance is useful for reading the last part of a
41 : * file in an efficient manner, when it is not known exactly how far
42 : * back (towards the front!) to go, to find the start of the desired
43 : * data block.
44 : *
45 : * BackBuffer is an std::vector<unsigned char> that fills
46 : * itself with data from a file by reading chunks from the end of the
47 : * file progressing towards the start. Upon construction the
48 : * BackBuffer instance is associated with a file and a chunksize can
49 : * be specified. To read a chunk of the file into the BackBuffer call
50 : * readChunk().
51 : *
52 : * Note that this is not a good option for really large files as the
53 : * buffer is enlarged using an insert() on each call to readChunk().
54 : */
55 :
56 :
57 :
58 : /** BackBuffer constructor.
59 : *
60 : * Initialize a buffer that reads data backward.
61 : *
62 : * The content of the buffer is defined as the content of the \p is
63 : * stream. The stream must be open and seekable. The file pointer
64 : * of the stream is modified by this constructor and on each
65 : * call to readChunk().
66 : *
67 : * \exception IOException
68 : * This exception is raised when the VirtualSeeker (\p vs) returns
69 : * an invalid answer determining the size of the stream.
70 : *
71 : * \param[in,out] is The istream to read the data from. The stream must
72 : * be seekable, as BackBuffer will reposition the
73 : * file pointer to read chunks from the back of the
74 : * file.
75 : * \param[in] vs The virtual seeker used to change the file pointer.
76 : * \param[in] chunk_size Specifies the size of the chunks to read the
77 : * file into the BackBuffer in.
78 : */
79 391 : BackBuffer::BackBuffer(std::istream& is, VirtualSeeker const& vs, ssize_t const chunk_size)
80 : : m_vs(vs)
81 : , m_chunk_size(chunk_size)
82 : , m_is(is)
83 410 : , m_file_pos(0)
84 : {
85 391 : if(!m_is)
86 : {
87 1 : throw InvalidException("BackBuffer() initialized with an invalid input stream.");
88 : }
89 390 : if(m_chunk_size <= 0)
90 : {
91 17 : throw InvalidException("Invalid chunk_size parameter, it has to be larger than zero.");
92 : }
93 :
94 373 : m_vs.vseekg(m_is, 0, std::ios::end);
95 373 : m_file_pos = m_vs.vtellg(m_is);
96 :
97 : // The following should only happens when m_vs.startOffset() is a
98 : // position in the file that lies after m_vs.endOffset(), which
99 : // is clearly not a valid situation. However, vtellg() may just
100 : // fail too.
101 373 : if(m_file_pos < 0)
102 : {
103 1 : throw IOException("Invalid virtual file endings.");
104 : }
105 372 : }
106 :
107 :
108 : /** \brief Read a chunk of data.
109 : *
110 : * Reads another chunk and returns the size of the chunk that has
111 : * been read. Returns 0 on I/O failure.
112 : *
113 : * When a new chunk is read in the already stored bytes change
114 : * position in the BackBuffer. \p read_pointer is assumed by
115 : * readChunk() to be a pointer into a position in the BackBuffer,
116 : * and is updated to point to the same position in the file
117 : * as it pointed to before the new chunk was prepended.
118 : *
119 : * When first calling readChunk(), \p read_pointer is expected
120 : * to be zero and represent the position of EOF.
121 : *
122 : * \warning
123 : * The file is read backward.
124 : *
125 : * \warning
126 : * The function may change m_chunk_size. In the end will be
127 : * zero and no more data can be read.
128 : *
129 : * \warning
130 : * The function changes the file pointer of the attached stream.
131 : *
132 : * \bug
133 : * We may want to throw an error if m_is.is_good() returns false.
134 : *
135 : * \param[in,out] read_pointer The buffer pointer.
136 : *
137 : * \return The number of bytes read if any, otherwise zero. Note that if
138 : * an error occurs, the function also returns zero.
139 : */
140 407 : ssize_t BackBuffer::readChunk(ssize_t& read_pointer)
141 : {
142 : // Update m_chunk_size and file position
143 407 : m_chunk_size = std::min<ssize_t>(static_cast<ssize_t>(m_file_pos), m_chunk_size);
144 407 : m_file_pos -= m_chunk_size;
145 407 : m_vs.vseekg(m_is, m_file_pos, std::ios::beg);
146 :
147 : // Make space for m_chunk_size new bytes
148 407 : insert(begin(), m_chunk_size, static_cast<char>(0));
149 :
150 : // Read in the next m_chunk_size bytes
151 407 : zipRead(m_is, *this, m_chunk_size);
152 407 : read_pointer += m_chunk_size;
153 :
154 407 : return m_is.good() ? m_chunk_size : 0;
155 : }
156 :
157 :
158 3 : } // zipios namespace
159 : // Local Variables:
160 : // mode: cpp
161 : // indent-tabs-mode: nil
162 : // c-basic-offset: 4
163 : // tab-width: 4
164 : // End:
165 :
166 : // vim: ts=4 sw=4 et
|