rapidyaml  0.16.0
parseandemitYAML,anddoitfast
format_base64.hpp
Go to the documentation of this file.
1 #ifndef C4_FORMAT_BASE64_HPP_
2 #define C4_FORMAT_BASE64_HPP_
3 
4 /** @file format_base64.hpp Utilities for formatting data as base64 */
5 
6 #ifndef C4_SUBSTR_HPP_
7 #include "c4/substr.hpp"
8 #endif
9 #ifndef C4_BLOB_HPP_
10 #include "c4/blob.hpp"
11 #endif
12 #ifndef C4_BASE64_HPP_
13 #include "c4/base64.hpp"
14 #endif
15 
16 
17 namespace c4 {
18 namespace fmt {
19 
20 /** @defgroup doc_base64_fmt Base64 format specifiers
21  *
22  * ```c++
23  * // given these variables:
24  * T var = {};
25  * T &ref = var;
26  * std::vector<T> vec = ...;
27  * substr buf = ...;
28  *
29  * // there are different approaches to encode/decode base64:
30  * size_t numchars = to_chars(buf, base64(var)); // encode var as base64
31  * size_t numchars = to_chars(buf, base64(ref)); // same as above
32  * size_t numchars = to_chars(buf, base64(&var, 1)); // same as above
33  * size_t numchars = to_chars(buf, base64(vec.data(), vec.size())); // for the container, but same call form as above
34  * size_t numchars = to_chars(buf, base64(vec)); // same effect as prev call
35  *
36  * // using cbase64() (the `c` prefix is for const) is equivalent
37  * // for encoding, but quicker to compile:
38  * size_t numchars = to_chars(buf, cbase64(var)); // encode var as base64
39  * size_t numchars = to_chars(buf, cbase64(ref)); // same as above
40  * size_t numchars = to_chars(buf, cbase64(&var, 1)); // same as above
41  * size_t numchars = to_chars(buf, cbase64(vec.data(), vec.size())); // for the container, but same call as above
42  * size_t numchars = to_chars(buf, cbase64(vec)); // same effect as prev call
43  *
44  * // to decode:
45  * csubstr buf = ...;
46  * size_t reqbytes = 0; // number of bytes of decoded data
47  * bool ok = from_chars(buf, base64(var)); // decode base64 to var
48  * bool ok = from_chars(buf, base64(var, &reqbytes)); // same. reqbytes will be sizeof(var)
49  * bool ok = from_chars(buf, base64(ref)); // same as above
50  * bool ok = from_chars(buf, base64(ref, &reqbytes)); // same. reqbytes will be sizeof(var)
51  * bool ok = from_chars(buf, base64(&var, 1, &reqbytes)); // same
52  * bool ok = from_chars(buf, base64(vec.data(), vec.size(), &reqbytes)); // for the container, but same call as above
53  * bool ok = from_chars(buf, base64(vec, &reqbytes)); // same effect as prev call
54  * ```
55  * @ingroup doc_format_specifiers
56  * @ingroup doc_base64
57  * @{ */
58 
59 /** @cond dev */
60 namespace detail {
61 template<class> struct sfinae_true : std::true_type{};
62 template<class T, class A0> static auto test_resize(int) -> sfinae_true<decltype(std::declval<T>().resize(std::declval<A0>()))>;
63 template<class, class A0> static auto test_resize(int64_t) -> std::false_type;
64 /// a traits class to report when a type as a member method named resize
65 /// @see https://stackoverflow.com/a/9154394
66 template<class T, class Arg=size_t>
67 struct has_resize : decltype(detail::test_resize<typename std::remove_cv<typename std::remove_reference<typename std::remove_pointer<T>::type>::type>::type, Arg>(0)){};
68 
69 template<typename CharOrConstChar>
70 struct base64_wrapper_
71 {
72  blob_<CharOrConstChar> data;
73  size_t *required_size;
74  base64_wrapper_(blob_<CharOrConstChar> blob, size_t *len_=nullptr) noexcept
75  : data(blob)
76  , required_size(len_)
77  {}
78 };
79 
80 template<class Container, typename CharOrConstChar>
81 struct base64_container_wrapper_
82 {
83  using value_type = typename Container::value_type;
84  Container *container;
85  size_t *required_size;
86  base64_container_wrapper_(Container *c, size_t *len_=nullptr) noexcept
87  : container(c)
88  , required_size(len_)
89  {}
90  blob_<CharOrConstChar> data() const noexcept
91  {
92  size_t sz = container->size();
93  CharOrConstChar *first = sz ? reinterpret_cast<CharOrConstChar*>(&(*container)[0]) : nullptr; // NOLINT
94  return blob_<CharOrConstChar>(first, sizeof(value_type) * sz);
95  }
96 };
97 } // namespace detail
98 /** @endcond */
99 
100 
101 
102 /** a tag type to mark a payload to be encoded as base64 */
103 using const_base64_wrapper = detail::base64_wrapper_<cbyte>;
104 /** a tag type to mark a payload to be decoded as base64 */
105 using base64_wrapper = detail::base64_wrapper_<byte>;
106 
107 
108 /** a tag type to mark a payload as base64-encoded */
109 template<class Container>
110 using const_base64_container_wrapper = detail::base64_container_wrapper_<const Container, cbyte>;
111 /** a tag type to mark a payload to be encoded as base64 */
112 template<class Container>
113 using base64_container_wrapper = detail::base64_container_wrapper_<Container, byte>;
114 
115 
116 /** a tag function to mark a csubstr payload to be encoded in base64 format */
117 C4_ALWAYS_INLINE const_base64_wrapper cbase64(csubstr s, size_t *reqsize=nullptr)
118 {
119  return const_base64_wrapper(cblob(s.str, s.len), reqsize);
120 }
121 /** a tag function to mark a csubstr payload to be encoded in base64 format */
122 C4_ALWAYS_INLINE const_base64_wrapper base64(csubstr s, size_t *reqsize=nullptr)
123 {
124  return const_base64_wrapper(cblob(s.str, s.len), reqsize);
125 }
126 /** a tag function to mark a variable to be decoded from base64 */
127 C4_ALWAYS_INLINE base64_wrapper base64(substr s, size_t *reqsize=nullptr)
128 {
129  return base64_wrapper(blob(s.str, s.len), reqsize);
130 }
131 
132 
133 /** a tag function to mark a payload to be encoded in base64 format */
134 template<class T>
135 C4_ALWAYS_INLINE const_base64_wrapper cbase64(T const* arg, size_t sz, size_t *reqsize=nullptr) // NOLINT
136 {
137  return const_base64_wrapper(cblob(arg, sz), reqsize);
138 }
139 /** a tag function to mark a payload to be encoded in base64 format */
140 template<class T>
141 C4_ALWAYS_INLINE auto base64(T * arg, size_t sz, size_t *reqsize=nullptr) // NOLINT
142  -> typename std::conditional<std::is_const<typename std::remove_reference<typename std::remove_pointer<T>::type>::type>::value,
144  base64_wrapper>::type
145 {
146  using U = typename std::remove_reference<typename std::remove_pointer<T>::type>::type;
147  using ret_type = typename std::conditional<std::is_const<U>::value,
149  base64_wrapper>::type;
150  using blob_type = typename std::conditional<std::is_const<U>::value,
151  cblob,
152  blob>::type;
153  return ret_type(blob_type(arg, sz), reqsize);
154 }
155 
156 
157 /** a tag function to mark a payload to be encoded in base64 format */
158 template<class T>
159 C4_ALWAYS_INLINE auto cbase64(T const& arg, size_t *reqsize=nullptr) // NOLINT
160  -> typename std::enable_if< ! detail::has_resize<T>::value, const_base64_wrapper>::type
161 {
162  return const_base64_wrapper(cblob(arg), reqsize);
163 }
164 /** a tag function to mark a payload to be encoded or decoded in base64 format */
165 template<class T>
166 C4_ALWAYS_INLINE auto base64(T & arg, size_t *reqsize=nullptr) // NOLINT
167  -> typename std::enable_if< ! detail::has_resize<T>::value,
168  typename std::conditional<std::is_const<typename std::remove_reference<typename std::remove_pointer<T>::type>::type>::value,
170  base64_wrapper>::type>::type
171 {
172  using U = typename std::remove_reference<typename std::remove_pointer<T>::type>::type;
173  using ret_type = typename std::conditional<std::is_const<U>::value,
175  base64_wrapper>::type;
176  using blob_type = typename std::conditional<std::is_const<U>::value,
177  cblob,
178  blob>::type;
179  return ret_type(blob_type(arg), reqsize);
180 }
181 
182 /** a tag function to mark a container (payload with a .resize()
183  * method) to be encoded in base64 format. */
184 template<class T>
185 C4_ALWAYS_INLINE auto cbase64(T const& arg, size_t *reqsize=nullptr) // NOLINT
186  -> typename std::enable_if<detail::has_resize<T>::value, const_base64_container_wrapper<T>>::type
187 {
188  return const_base64_container_wrapper<T>(&arg, reqsize);
189 }
190 /** a tag function to mark a container (payload with a .resize()
191  * method) to be encoded or decoded in base64 format. Subsequently
192  * when decoding, from_chars() will resize the container to fit the
193  * decoded data. */
194 template<class T>
195 C4_ALWAYS_INLINE auto base64(T & arg, size_t *reqsize=nullptr) // NOLINT
196  -> typename std::enable_if<detail::has_resize<T>::value,
197  typename std::conditional<std::is_const<typename std::remove_reference<typename std::remove_pointer<T>::type>::type>::value,
198  const_base64_container_wrapper<T>,
199  base64_container_wrapper<T>>::type>::type
200 {
201  using ret_type = typename std::conditional<std::is_const<typename std::remove_reference<typename std::remove_pointer<T>::type>::type>::value,
202  const_base64_container_wrapper<T>,
203  base64_container_wrapper<T>>::type;
204  return ret_type(&arg, reqsize);
205 }
206 
207 /** @} */ // base64_fmt
208 
209 } // namespace fmt
210 
211 
212 //-----------------------------------------------------------------------------
213 
214 /** @addtogroup doc_base64
215  * @{ */
216 
217 /** (1) read a variable in base64 format
218  * @ingroup doc_from_chars */
219 inline bool from_chars(csubstr buf, fmt::base64_wrapper const& b)
220 {
221  size_t reqsize = 0;
222  bool ok = base64_decode(buf.str, buf.len, b.data.buf, b.data.len, &reqsize);
223  if(b.required_size)
224  *b.required_size = reqsize;
225  return ok;
226 }
227 /** (2) read a variable in base64 format
228  * @ingroup doc_from_chars */
230 {
231  return from_chars(buf, *b);
232 }
233 
234 
235 /** write a variable or buffer in base64 format
236  * @ingroup doc_to_chars */
237 template<typename CharOrConstChar>
238 inline size_t to_chars(substr buf, fmt::detail::base64_wrapper_<CharOrConstChar> const& b)
239 {
240  size_t reqsize = base64_encode(buf.str, buf.len, b.data.buf, b.data.len);
241  if(b.required_size)
242  *b.required_size = reqsize;
243  return reqsize;
244 }
245 /** write a container in base64 format
246  * @ingroup doc_to_chars */
247 template<class Container, typename CharOrConstChar>
248 size_t to_chars(substr buf, fmt::detail::base64_container_wrapper_<Container, CharOrConstChar> const& b)
249 {
250  cblob data = b.data();
251  size_t reqsize = base64_encode(buf.str, buf.len, data.buf, data.len);
252  if(b.required_size)
253  *b.required_size = reqsize;
254  return reqsize;
255 }
256 
257 /** read a container in base64 format, resizing it as needed to
258  * accomodate the result
259  * @ingroup doc_from_chars */
260 template<class T>
262 {
263  enum : size_t { elm_sz = sizeof(typename fmt::base64_container_wrapper<T>::value_type) }; // NOLINT
264  blob data = b.data();
265  size_t required_size = 0;
266  bool ok = base64_decode(buf.str, buf.len, data.buf, data.len, &required_size);
267  if(b.required_size)
268  *b.required_size = required_size;
269  if(!required_size)
270  return ok;
271  else if(!ok && ((required_size < data.len) || (required_size % elm_sz)))
272  return false;
273  size_t num_elms = required_size / elm_sz;
274  b.container->resize(num_elms);
275  if(required_size > data.len)
276  {
277  data = b.data();
278  ok = base64_decode(buf.str, buf.len, data.buf, data.len, &required_size);
279  if(b.required_size)
280  *b.required_size = required_size;
281  }
282  return ok;
283 }
284 /** read a container in base64 format, resizing it as needed to
285  * accomodate the result
286  * @ingroup doc_from_chars */
287 template<class T>
289 {
290  return from_chars(buf, *b);
291 }
292 
293 /** @} */ // base64
294 
295 } // namespace c4
296 
297 #endif /* C4_FORMAT_BASE64_HPP_ */
detail::base64_wrapper_< byte > base64_wrapper
a tag type to mark a payload to be decoded as base64
encoding/decoding for base64.
C * str
a restricted pointer to the first character of the substring
Definition: substr.hpp:216
read+write string views
bool from_chars(csubstr buf, uint8_t *v) noexcept
Definition: charconv.hpp:2383
size_t len
the length of the substring
Definition: substr.hpp:218
detail::base64_container_wrapper_< const Container, cbyte > const_base64_container_wrapper
a tag type to mark a payload as base64-encoded
detail::base64_wrapper_< cbyte > const_base64_wrapper
a tag type to mark a payload to be encoded as base64
size_t base64_encode(char *encoded, size_t encoded_sz, void const *data, size_t data_sz)
base64-encode binary data.
const_base64_wrapper base64(csubstr s, size_t *reqsize=nullptr)
a tag function to mark a csubstr payload to be encoded in base64 format
bool base64_decode(char const *encoded, size_t encoded_sz, void *data, size_t data_sz, size_t *data_sz_required)
decode the base64 encoding in the given buffer.
Definition: base64.cpp:412
detail::base64_container_wrapper_< Container, byte > base64_container_wrapper
a tag type to mark a payload to be encoded as base64
const_base64_wrapper cbase64(csubstr s, size_t *reqsize=nullptr)
a tag function to mark a csubstr payload to be encoded in base64 format
size_t to_chars(substr buf, uint8_t v) noexcept
Definition: charconv.hpp:2342