OTest2
A C++ testing framework
base64istream.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 #include <base64istream.h>
20 
21 #include <assert.h>
22 #include <cctype>
23 #include <cstdint>
24 #include <streambuf>
25 
26 #include <utils.h>
27 
28 namespace OTest2 {
29 
30 class Base64IStream::Buffer : public std::streambuf {
31  private:
32  std::istream* decorated;
33 
34  static const int base64_index[256];
35 
36  enum { BUFFER_SIZE = 3 * 26 };
37  char buffer[BUFFER_SIZE];
38  bool eof;
39 
40  public:
41  explicit Buffer(
42  std::istream* decorated_);
43  virtual ~Buffer();
44 
45  /* -- avoid copying */
46  Buffer(
47  const Buffer&) = delete;
49  const Buffer&) = delete;
50 
51  private:
52  virtual int underflow() override;
53 
54  bool readQuaternion(
55  std::uint8_t quaternion_[]);
56 };
57 
58 const int Base64IStream::Buffer::base64_index[256] = {
59  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
60  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
61  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
62  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
63  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
64  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
65  -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
66  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
67 };
68 
70  std::istream* decorated_) :
71  decorated(decorated_),
72  eof(false) {
73  assert(decorated != nullptr);
74 
75 }
76 
78 
79 }
80 
81 bool Base64IStream::Buffer::readQuaternion(
82  std::uint8_t quaternion_[]) {
83  int i_(0);
84  while(i_ < 4) {
85  int ci_(decorated->get());
86 
87  /* -- end of file -> no input data or missing padding */
88  if(ci_ == traits_type::eof())
89  break;
90 
91  /* -- skip whitespaces */
92  std::uint8_t c_(static_cast<std::uint8_t>(traits_type::to_char_type(ci_)));
93  if(std::isspace(c_))
94  continue;
95 
96  /* -- invalid character */
97  if(c_ != '=' && base64_index[c_] == -1)
98  break;
99 
100  /* -- store the character */
101  quaternion_[i_++] = c_;
102  }
103 
104  return i_ == 4;
105 }
106 
107 int Base64IStream::Buffer::underflow() {
108 
109  /* -- we have already reached the end of the sequence */
110  if(eof) {
111  setg(nullptr, nullptr, nullptr);
112  return traits_type::eof();
113  }
114 
115  char* out_(buffer);
116  char const* const end_(buffer + BUFFER_SIZE);
117  while(out_ < end_) {
118  /* -- read data from the decorated buffer */
119  std::uint8_t quaternion_[4];
120  if(!readQuaternion(quaternion_)) {
121  /* -- There are no other data or the stream is not correctly padded. */
122  eof = true;
123  break;
124  }
125 
126  if(quaternion_[3] != '=') {
127  /* -- full quaternion */
128  std::uint32_t decoded_(
129  base64_index[quaternion_[0]] << 18
130  | base64_index[quaternion_[1]] << 12
131  | base64_index[quaternion_[2]] << 6
132  | base64_index[quaternion_[3]]);
133  *out_++ = decoded_ >> 16 & 0xff;
134  *out_++ = decoded_ >> 8 & 0xff;
135  *out_++ = decoded_ & 0xff;
136  }
137  else {
138  /* -- padding */
139  std::uint32_t decoded_(
140  base64_index[quaternion_[0]] << 18
141  | base64_index[quaternion_[1]] << 12);
142  *out_++ = decoded_ >> 16 & 0xff;
143  if(quaternion_[2] != '=') {
144  decoded_ |= base64_index[quaternion_[2]] << 6;
145  *out_++ = decoded_ >> 8 & 0xff;
146  }
147  eof = true;
148  break;
149  }
150  }
151 
152  /* -- Reset the stream buffer pointers. Even if we reached the end of
153  * the sequence, there still can be rest of data. */
154  setg(buffer, buffer, out_);
155 
156  /* -- return next sequence character */
157  if(buffer != out_)
158  return traits_type::to_int_type(*buffer);
159  else
160  return traits_type::eof();
161 }
162 
164  std::istream* decorated_) :
165  buffer(new Buffer(decorated_)) {
166  rdbuf(buffer);
167 }
168 
170  odelete(buffer);
171 }
172 
173 } /* -- namespace OTest2 */
OTest2::Base64IStream::~Base64IStream
virtual ~Base64IStream()
Dtor.
Definition: base64istream.cpp:169
base64istream.h
OTest2::Base64IStream::Buffer::Buffer
Buffer(std::istream *decorated_)
Definition: base64istream.cpp:69
OTest2::Base64IStream::Buffer::~Buffer
virtual ~Buffer()
Definition: base64istream.cpp:77
utils.h
OTest2::Base64IStream::Buffer::operator=
Buffer & operator=(const Buffer &)=delete
OTest2
Definition: assertbean.h:25
OTest2::Base64IStream::Buffer
Definition: base64istream.cpp:30
OTest2::Base64IStream::Base64IStream
Base64IStream(std::istream *decorated_)
Ctor.
Definition: base64istream.cpp:163
OTest2::odelete
void odelete(T_ *&object_)
Delete a pointer and set it invalid.
Definition: utils.h:34