Line data Source code
1 : // Copyright (c) 2019-2026 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 : #pragma once
19 :
20 : /** \file
21 : * \brief Create a stream from a memory buffer.
22 : *
23 : * At times we have a memory buffer that we need to pass as a stream.
24 : * This template can be used to transform that buffer in a streambuf,
25 : * which can then be passed to a stream on creation or through rdbuf().
26 : */
27 :
28 : // snapdev
29 : //
30 : #include <snapdev/not_reached.h>
31 : #include <snapdev/not_used.h>
32 :
33 :
34 : // C++
35 : //
36 : #include <iostream>
37 : #include <ostream>
38 : //#include <sstream>
39 : //#include <string>
40 :
41 :
42 :
43 : namespace snapdev
44 : {
45 :
46 :
47 :
48 : /** \brief Create a streambuf where the data comes from a memory buffer.
49 : *
50 : * This class is used to create a streambuf from a memory buffer.
51 : * If you are looking at capturing the output of a stream, you may be
52 : * interested by the ostream_to_buf instead. That other implementation
53 : * allows you to save the output of a stream in a string and then verify
54 : * that string.
55 : *
56 : * This implementation was created for input rather than output, allowing
57 : * you to create a buffer from a memory buffer.
58 : *
59 : * \code
60 : * {
61 : * // Say you have a vector with data
62 : * char data[]{ 1, 2, 3, 4, 5, 6, 7 };
63 : *
64 : * // you create a vector_streambuf this way
65 : * snapdev::memory_streambuf buf(data, sizeof(data));
66 : *
67 : * // and then an input stream this way
68 : * std::istream in(&buf);
69 : *
70 : * // now you can read & seek as usual
71 : *
72 : * // if you want to be able to do updates, use an iostream insead
73 : * std::iostream in_out(&buf);
74 : * }
75 : * \endcode
76 : *
77 : * \note
78 : * The function expects the memory buffer to only contain data that can
79 : * be read/written as such. This means no pointers, not classes with
80 : * virtual functions, etc.
81 : *
82 : * \warning
83 : * The memory buffer must remain available and not change in size.
84 : *
85 : * \tparam CharT The type of characters used by the stream.
86 : * \tparam Traits The traits of the specified character type.
87 : */
88 : template<
89 : typename CharT = char
90 : , typename Traits = std::char_traits<CharT>
91 : > class memory_streambuf
92 : : public std::basic_streambuf<CharT, Traits>
93 : {
94 : public:
95 : typedef CharT char_type;
96 : typedef Traits traits_type;
97 : typedef typename Traits::int_type int_type;
98 : typedef typename Traits::pos_type pos_type;
99 : typedef typename Traits::off_type off_type;
100 :
101 : typedef std::basic_streambuf<char_type, traits_type> streambuf_type;
102 :
103 : /** \brief Initialize the streambuf from \p data.
104 : *
105 : * This function creates a streambuf that you can then use to initialize
106 : * an input or output stream with.
107 : *
108 : * This version of the constructor allows to read and write to the
109 : * stream buffer (a.k.a. the vector). You can use it with input or
110 : * an output stream.
111 : *
112 : * Note that the reading and writing happen from the start of the
113 : * buffer unless a corresponding seek is used.
114 : *
115 : * \todo
116 : * Note that the overflow() function does not yet know how to grow
117 : * the vector. If you attempt to write past the end, an exception
118 : * is generated.
119 : *
120 : * \param[in] data A pointer to a memory buffer.
121 : * \param[in] size The size of the data buffer in bytes.
122 : */
123 2 : memory_streambuf(void * data, std::size_t size)
124 2 : {
125 2 : char * ptr(reinterpret_cast<char *>(data));
126 2 : char_type * begin(reinterpret_cast<char_type *>(ptr));
127 2 : char_type * end(reinterpret_cast<char_type *>(ptr + size));
128 2 : this->setg(begin, begin, end);
129 2 : this->setp(begin, end);
130 2 : }
131 :
132 : /** \brief Initialize the streambuf from \p vec.
133 : *
134 : * This function creates a read-only streambuf that you can then use
135 : * to initialize an input stream with. Trying to write to that stream
136 : * generates an exception.
137 : *
138 : * \param[in] data A pointer to a memory buffer.
139 : * \param[in] size The size of the data buffer in bytes.
140 : */
141 1 : memory_streambuf(void const * data, std::size_t size)
142 1 : : f_read_only(true)
143 : {
144 1 : char * ptr(const_cast<char *>(reinterpret_cast<char const *>(data)));
145 1 : char_type * begin(reinterpret_cast<char_type *>(ptr));
146 1 : char_type * end(reinterpret_cast<char_type *>(ptr + size));
147 1 : this->setg(begin, begin, end);
148 :
149 : // a write calls overflow() if the start & end pointers are the same
150 : //
151 1 : this->setp(begin, begin);
152 1 : }
153 :
154 : protected:
155 : /** \brief Function called when an overflow on a put() happens.
156 : *
157 : * This function is called if a put() attempts to write past the
158 : * end of the buffer. In that case, we throw an exception. It is
159 : * not possible for us to grow the buffer.
160 : *
161 : * \param[in] c The character to output.
162 : *
163 : * \return The input character \p c or '\0' of \p c is EOF.
164 : */
165 3 : int_type overflow(int_type c)
166 : {
167 3 : snapdev::NOT_USED(c);
168 3 : throw std::ios_base::failure("this buffer is read-only, writing to the buffer is not available.");
169 : snapdev::NOT_REACHED();
170 : }
171 :
172 45 : virtual pos_type seekoff(
173 : off_type const offset
174 : , std::ios_base::seekdir const dir
175 : , std::ios_base::openmode const mode) override
176 : {
177 45 : off_type result(-1);
178 :
179 45 : if((mode & std::ios_base::in) != 0)
180 : {
181 32 : char_type * pos = nullptr;
182 32 : switch(dir)
183 : {
184 24 : case std::ios_base::cur:
185 24 : pos = this->gptr() + offset;
186 24 : break;
187 :
188 1 : case std::ios_base::end:
189 1 : pos = this->egptr() + offset;
190 1 : break;
191 :
192 6 : case std::ios_base::beg:
193 6 : pos = this->eback() + offset;
194 6 : break;
195 :
196 1 : default:
197 1 : throw std::ios_base::failure("unknown direction in seekoff() -- in");
198 :
199 : }
200 31 : if(pos < this->eback())
201 : {
202 1 : pos = this->eback();
203 : }
204 31 : if(pos > this->egptr())
205 : {
206 1 : pos = this->egptr();
207 : }
208 31 : this->setg(this->eback(), pos, this->egptr());
209 31 : result = pos - this->eback();
210 : }
211 :
212 44 : if((mode & std::ios_base::out) != 0)
213 : {
214 13 : char_type * pos = nullptr;
215 13 : switch(dir)
216 : {
217 7 : case std::ios_base::cur:
218 7 : pos = this->pptr() + offset;
219 7 : break;
220 :
221 1 : case std::ios_base::end:
222 1 : pos = this->epptr() + offset;
223 1 : break;
224 :
225 4 : case std::ios_base::beg:
226 4 : pos = this->pbase() + offset;
227 4 : break;
228 :
229 1 : default:
230 1 : throw std::ios_base::failure("unknown direction in seekoff() -- out");
231 :
232 : }
233 12 : if(pos < this->pbase())
234 : {
235 1 : pos = this->pbase();
236 : }
237 12 : if(pos > this->epptr())
238 : {
239 2 : pos = this->epptr();
240 : }
241 12 : this->setp(this->pbase(), this->epptr());
242 12 : result = pos - this->pbase();
243 12 : this->pbump(result);
244 : }
245 :
246 86 : return result;
247 : }
248 :
249 10 : virtual pos_type seekpos(pos_type offset, std::ios_base::openmode mode) override
250 : {
251 10 : return seekoff(offset, std::ios_base::beg, mode);
252 : }
253 :
254 : private:
255 : /** \brief Whether the buffer is considered read-only or not.
256 : *
257 : * When creating the streambuf with a constant memory buffer, this
258 : * parameter is set to true. This prevents writes to the file. In
259 : * other words, the memory will never be updated.
260 : */
261 : bool f_read_only = false;
262 : };
263 :
264 :
265 :
266 : } // namespace snapdev
267 : // vim: ts=4 sw=4 et
|