OTest2
A C++ testing framework
base64ostream.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020 Ondrej Starek
3  *
4  * This file is part of OTest2.
5  *
6  * OTest2 is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License,
9  * or (at your option) any later version.
10  *
11  * OTest2 is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14  * License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with OTest2. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <base64ostream.h>
21 
22 #include <assert.h>
23 #include <cstdint>
24 #include <streambuf>
25 
26 #include <utils.h>
27 
28 namespace OTest2 {
29 
33 class Base64OStream::Buffer : public std::streambuf {
34  private:
35  std::ostream* decorated;
36 
37  int output_raw_width;
38  int output_base64_width;
39  bool output_newline;
40  char* output_buffer;
41 
42  public:
52  explicit Buffer(
53  std::ostream* decorated_,
54  int width_);
55 
59  virtual ~Buffer();
60 
61  /* -- avoid copying */
62  Buffer(
63  const Buffer&) = delete;
65  const Buffer&) = delete;
66 
75  bool finish() noexcept;
76 
77  private:
78  /* -- streambuf interface */
79  virtual int overflow(
80  int c) override;
81 
82  void finishData();
83 };
84 
85 
87  std::ostream* decorated_,
88  int width_) :
89  decorated(decorated_) {
90  assert(decorated != nullptr);
91 
92  /* -- Create output buffer with size according to requested line width. */
93  if(width_ > 0) {
94  assert(width_ % 4 == 0);
95  output_base64_width = width_;
96  output_newline = true;
97  }
98  else {
99  output_base64_width = 80;
100  output_newline = false;
101  }
102  output_raw_width = output_base64_width / 4 * 3;
103  output_buffer = new char[output_raw_width];
104 
105  /* -- set the streambuf pointers */
106  setp(output_buffer, output_buffer + output_raw_width);
107 }
108 
110  finishData();
111  delete[] output_buffer;
112 }
113 
114 void Base64OStream::Buffer::finishData() {
115  /* -- compute length of data */
116  auto length_(pptr() - output_buffer);
117  assert(length_ >= 0 && length_ <= output_raw_width);
118 
119  /* -- encode the output buffer */
120  static const unsigned char base64_table[] =
121  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
122  char buffer_[output_base64_width];
123  const std::uint8_t* in_(reinterpret_cast<std::uint8_t*>(output_buffer));
124  char* out_(buffer_);
125  while(length_ >= 3) {
126  *out_++ = base64_table[in_[0] >> 2];
127  *out_++ = base64_table[((in_[0] & 0x03) << 4) | (in_[1] >> 4)];
128  *out_++ = base64_table[((in_[1] & 0x0f) << 2) | (in_[2] >> 6)];
129  *out_++ = base64_table[in_[2] & 0x3f];
130  length_ -= 3;
131  in_ += 3;
132  }
133  if(length_ > 0) {
134  /* -- Padding should happen only if the sequence is closed. Otherwise,
135  * the data length is divisible by 3. */
136  *out_++ = base64_table[in_[0] >> 2];
137  if(length_ == 1) {
138  *out_++ = base64_table[(in_[0] & 0x03) << 4];
139  *out_++ = '=';
140  }
141  else {
142  *out_++ = base64_table[((in_[0] & 0x03) << 4) | (in_[1] >> 4)];
143  *out_++ = base64_table[(in_[1] & 0x0f) << 2];
144  }
145  *out_++ = '=';
146  }
147 
148  /* -- reset the buffer pointers */
149  setp(output_buffer, output_buffer + output_raw_width);
150 
151  /* -- push the encoded data into the decorated stream buffer */
152  auto coded_length_(out_ - buffer_);
153  decorated->write(buffer_, static_cast<std::streamsize>(coded_length_));
154 }
155 
156 int Base64OStream::Buffer::overflow(
157  int c) {
158  /* -- flush current data */
159  finishData();
160 
161  /* -- start new line if it's requested */
162  if(output_newline)
163  decorated->put('\n');
164 
165  /* -- write the character */
166  if(c != traits_type::eof()) {
167  *output_buffer = traits_type::to_char_type(c);
168  pbump(1);
169  }
170 
171  return traits_type::not_eof(c);
172 }
173 
175  finishData();
176  return true;
177 }
178 
180  std::ostream* decorated_,
181  int line_length_) :
182  buffer(new Buffer(decorated_, line_length_)) {
183  rdbuf(buffer);
184 }
185 
187  odelete(buffer);
188 }
189 
190 bool Base64OStream::finish() noexcept {
191  return buffer->finish();
192 }
193 
194 } /* -- namespace OTest2 */
OTest2::Base64OStream::finish
bool finish() noexcept
Finish current sequence.
Definition: base64ostream.cpp:190
OTest2::Base64OStream::Buffer::~Buffer
virtual ~Buffer()
Dtor.
Definition: base64ostream.cpp:109
base64ostream.h
OTest2::Base64OStream::~Base64OStream
virtual ~Base64OStream()
Dtor.
Definition: base64ostream.cpp:186
utils.h
OTest2::Base64OStream::Buffer::Buffer
Buffer(std::ostream *decorated_, int width_)
Ctor.
Definition: base64ostream.cpp:86
OTest2
Definition: assertbean.h:25
OTest2::Base64OStream::Buffer::operator=
Buffer & operator=(const Buffer &)=delete
OTest2::Base64OStream::Buffer
Base64 output stream buffer.
Definition: base64ostream.cpp:33
OTest2::Base64OStream::Base64OStream
Base64OStream(std::ostream *decorated_, int line_length_=0)
Ctor.
Definition: base64ostream.cpp:179
OTest2::Base64OStream::Buffer::finish
bool finish() noexcept
Finish currently opened sequence.
Definition: base64ostream.cpp:174
OTest2::odelete
void odelete(T_ *&object_)
Delete a pointer and set it invalid.
Definition: utils.h:34