Line data Source code
1 : /*
2 : * Copyright (c) 2013-2014: G-CSC, Goethe University Frankfurt
3 : * Author: Torbjoern Klatt, Martin Scherer
4 : *
5 : * This file is part of UG4.
6 : *
7 : * UG4 is free software: you can redistribute it and/or modify it under the
8 : * terms of the GNU Lesser General Public License version 3 (as published by the
9 : * Free Software Foundation) with the following additional attribution
10 : * requirements (according to LGPL/GPL v3 §7):
11 : *
12 : * (1) The following notice must be displayed in the Appropriate Legal Notices
13 : * of covered and combined works: "Based on UG4 (www.ug4.org/license)".
14 : *
15 : * (2) The following notice must be displayed at a prominent place in the
16 : * terminal output of covered works: "Based on UG4 (www.ug4.org/license)".
17 : *
18 : * (3) The following bibliography is recommended for citation and must be
19 : * preserved in all covered files:
20 : * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively
21 : * parallel geometric multigrid solver on hierarchically distributed grids.
22 : * Computing and visualization in science 16, 4 (2013), 151-164"
23 : * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel
24 : * flexible software system for simulating pde based models on high performance
25 : * computers. Computing and visualization in science 16, 4 (2013), 165-179"
26 : *
27 : * This program is distributed in the hope that it will be useful,
28 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 : * GNU Lesser General Public License for more details.
31 : */
32 :
33 : #include "common/util/base64_file_writer.h"
34 :
35 : // for base64 encoding with boost
36 : #include <boost/archive/iterators/transform_width.hpp>
37 : #include <boost/archive/iterators/base64_from_binary.hpp>
38 : #include <boost/archive/iterators/ostream_iterator.hpp>
39 : //#include <boost/filesystem.hpp>
40 :
41 : // debug includes!!
42 : #include "common/profiler/profiler.h"
43 : #include "common/error.h"
44 : #include "common/assert.h"
45 :
46 : using namespace std;
47 :
48 : /**
49 : * \brief This is the actual encoder using Boost iterators
50 : * note that final padding to triplet boundary has to be performed manually!
51 : * see: http://www.boost.org/doc/libs/1_48_0/libs/serialization/doc/dataflow.html
52 : */
53 : typedef boost::archive::iterators::base64_from_binary<
54 : // convert binary values to base64 characters
55 : boost::archive::iterators::transform_width<
56 : // retrieve 6 bit integers from a sequence of 8 bit bytes
57 : const char *, 6, 8>
58 : // compose all the above operations in to a new iterator
59 : > base64_text;
60 :
61 : namespace ug {
62 :
63 : ////////////////////////////////////////////////////////////////////////////////
64 : // PUBLIC FUNCTIONS
65 :
66 0 : Base64FileWriter::fmtflag Base64FileWriter::format() const {
67 0 : return m_currFormat;
68 : }
69 :
70 95 : Base64FileWriter& Base64FileWriter::operator<<(const fmtflag format)
71 : {
72 : PROFILE_FUNC();
73 :
74 : // forceful flushing of encoder's internal input buffer is necessary
75 : // if we are switching formats.
76 95 : if (format != m_currFormat && m_numBytesWritten > 0) {
77 1 : flushInputBuffer(true);
78 : }
79 95 : m_currFormat = format;
80 95 : return *this;
81 : }
82 :
83 31 : Base64FileWriter& Base64FileWriter::operator<<(int value)
84 : {
85 31 : dispatch(value);
86 31 : return *this;
87 : }
88 :
89 199 : Base64FileWriter& Base64FileWriter::operator<<(char value) {
90 199 : dispatch(value);
91 199 : return *this;
92 : }
93 :
94 8 : Base64FileWriter& Base64FileWriter::operator<<(const char* value)
95 : {
96 8 : dispatch(value);
97 8 : return *this;
98 : }
99 :
100 6 : Base64FileWriter& Base64FileWriter::operator<<(const string& value)
101 : {
102 6 : dispatch(value);
103 6 : return *this;
104 : }
105 :
106 6 : Base64FileWriter& Base64FileWriter::operator<<(float value)
107 : {
108 6 : dispatch(value);
109 6 : return *this;
110 : }
111 :
112 7 : Base64FileWriter& Base64FileWriter::operator<<(double value)
113 : {
114 7 : dispatch(value);
115 7 : return *this;
116 : }
117 :
118 12 : Base64FileWriter& Base64FileWriter::operator<<(long value)
119 : {
120 12 : dispatch(value);
121 12 : return *this;
122 : }
123 :
124 12 : Base64FileWriter& Base64FileWriter::operator<<(size_t value)
125 : {
126 12 : dispatch(value);
127 12 : return *this;
128 : }
129 :
130 0 : Base64FileWriter::Base64FileWriter() :
131 0 : m_currFormat(base64_ascii),
132 0 : m_inBuffer(ios_base::binary | ios_base::out | ios_base::in),
133 0 : m_lastInputByteSize(0),
134 0 : m_numBytesWritten(0)
135 0 : {}
136 :
137 98 : Base64FileWriter::Base64FileWriter(const char* filename,
138 98 : const ios_base::openmode mode) :
139 98 : m_currFormat(base64_ascii),
140 98 : m_inBuffer(ios_base::binary | ios_base::out | ios_base::in),
141 98 : m_lastInputByteSize(0),
142 98 : m_numBytesWritten(0)
143 : {
144 : PROFILE_FUNC();
145 :
146 98 : open(filename, mode);
147 98 : }
148 :
149 98 : Base64FileWriter::~Base64FileWriter()
150 : {
151 98 : flushInputBuffer(true);
152 98 : m_fStream.close();
153 98 : }
154 :
155 98 : void Base64FileWriter::open(const char *filename,
156 : const ios_base::openmode mode)
157 : {
158 : // TODO: Create non-existing subdirectories in a platform-independent way
159 : // like in the following code. (Problem: no header-only boost implementation!)
160 : /*
161 : if (mode & std::ios_base::out)
162 : {
163 : boost::filesystem::path p(filename);
164 : boost::filesystem::create_directories(p.parent_path());
165 : }
166 : */
167 :
168 98 : m_fStream.open(filename, mode);
169 98 : if (!m_fStream.is_open()) {
170 0 : UG_THROW( "Could not open output file: " << filename);
171 98 : } else if (!m_fStream.good()) {
172 0 : UG_THROW( "Can not write to output file: " << filename);
173 : }
174 98 : }
175 :
176 : ////////////////////////////////////////////////////////////////////////////////
177 : // PRIVATE FUNCTIONS
178 :
179 : // this function performs conversion to plain const char* and stores it in
180 : // m_inBuffer, either raw for binary mode or as string conversion in base64_ascii.
181 : template <typename T>
182 281 : void Base64FileWriter::dispatch(const T& value)
183 : {
184 : // PROFILE_FUNC(); // this profile node is too small
185 281 : assertFileOpen();
186 :
187 281 : switch ( m_currFormat ) {
188 187 : case base64_ascii:
189 : // create string representation of value and store it in buffer
190 187 : m_inBuffer << value;
191 : // written bytes is equivalent to current write pointer pos
192 187 : m_numBytesWritten = m_inBuffer.tellp();
193 187 : m_lastInputByteSize = sizeof(char);
194 : // check if a buffer flush is needed
195 187 : flushInputBuffer();
196 187 : break;
197 91 : case base64_binary: {
198 : // write the value in binary mode to the input buffer
199 : UG_ASSERT(m_inBuffer.good(), "can not write to buffer")
200 91 : m_inBuffer.write(reinterpret_cast<const char*>(&value), sizeof(T));
201 : UG_ASSERT(m_inBuffer.good(), "write failed")
202 :
203 91 : m_numBytesWritten += sizeof(T);
204 91 : m_lastInputByteSize = sizeof(T);
205 : // check if a buffer flush is needed
206 91 : flushInputBuffer();
207 91 : break;
208 : }
209 3 : case normal:
210 : // nothing to do here, almost
211 3 : m_fStream << value;
212 2 : break;
213 : }
214 281 : }
215 :
216 281 : inline void Base64FileWriter::assertFileOpen()
217 : {
218 281 : if (m_fStream.bad() || !m_fStream.is_open()) {
219 0 : UG_THROW( "File stream is not open." );
220 : }
221 281 : }
222 :
223 475 : void Base64FileWriter::flushInputBuffer(bool force)
224 : {
225 : // PROFILE_FUNC(); // this profile node is too small
226 :
227 : size_t buff_len = 0;
228 : // amount of elements to flush at once
229 : const uint elements_to_flush = 12;
230 : // in case of normal format, no input size is known, so this evals to zero.
231 475 : const uint bytes_to_flush = 3 * m_lastInputByteSize * elements_to_flush;
232 : // in case of forced flush we have to add padding
233 : uint paddChars = 0;
234 :
235 : // if force, flush all bytes in input stream
236 475 : if (force) {
237 197 : paddChars = (3 - m_numBytesWritten % 3) % 3;
238 : buff_len = m_numBytesWritten;
239 278 : } else if (bytes_to_flush <= m_numBytesWritten) {
240 : buff_len = bytes_to_flush;
241 : } else {
242 : // buff_len == 0
243 : return;
244 : }
245 :
246 : m_tmpBuff.clear();
247 : // enlarge the input stream by # paddChars because boost reads beyond buffer
248 203 : m_tmpBuff.resize(buff_len + paddChars, 0x0);
249 :
250 203 : if(!m_tmpBuff.empty()){
251 : char* buff = &m_tmpBuff[0];
252 :
253 103 : if (!m_inBuffer.good()) {
254 0 : UG_THROW("input buffer not good before read");
255 : }
256 :
257 : // read the buffer
258 103 : m_inBuffer.read(buff, buff_len);
259 :
260 103 : if (!m_inBuffer.good()) {
261 0 : UG_THROW("failed to read from input buffer");
262 : }
263 :
264 : // encode buff in base64
265 103 : copy(base64_text(buff), base64_text(buff + buff_len),
266 103 : boost::archive::iterators::ostream_iterator<char>(m_fStream));
267 : }
268 :
269 203 : size_t rest_len = m_numBytesWritten - buff_len;
270 :
271 203 : if (rest_len > 0) {
272 : m_tmpBuff.clear();
273 3 : m_tmpBuff.resize(rest_len);
274 : char* rest = &m_tmpBuff[0];
275 : // read the rest
276 3 : m_inBuffer.read(rest, rest_len);
277 3 : if (!m_inBuffer.good()) {
278 0 : UG_THROW("failed to read from input buffer");
279 : }
280 :
281 : // reset the internal string
282 3 : m_inBuffer.str("");
283 : // set write pos to beginning
284 3 : m_inBuffer.seekp(0, ios_base::beg);
285 :
286 : // and write the rest
287 3 : m_inBuffer.write(rest, rest_len);
288 3 : if (!m_inBuffer.good()) {
289 0 : UG_THROW("failed to write from input buffer");
290 : }
291 : } else {
292 : // reset buffer
293 200 : m_inBuffer.str("");
294 200 : m_inBuffer.seekp(0, ios_base::beg);
295 : }
296 :
297 : // set read and write position to beginning
298 203 : m_inBuffer.seekg(0, ios_base::beg);
299 :
300 203 : if (force) {
301 359 : for(uint i = 0; i < paddChars; ++i)
302 162 : m_fStream << '=';
303 :
304 : // resetting num bytes written and bytes in block
305 197 : m_numBytesWritten = 0;
306 : } else {
307 : // update amount of bytes in input buffer
308 6 : m_numBytesWritten -= buff_len;
309 : }
310 : }
311 :
312 98 : void Base64FileWriter::close()
313 : {
314 : PROFILE_FUNC();
315 :
316 : // make sure all remaining content of the input buffer is encoded and flushed
317 98 : flushInputBuffer(true);
318 :
319 : // only when this is done, close the file stream
320 98 : m_fStream.close();
321 : UG_ASSERT(m_fStream.good(), "could not close output file.");
322 98 : }
323 :
324 : } // namespace ug
|