App/Toponaming: Add base64 encoding to XML
This commit is contained in:
@@ -1,128 +1,162 @@
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
|
||||
Copyright (C) 2019 Zheng Lei (realthunder.dev@gmail.com)
|
||||
|
||||
* Added support of in-line transformation using boost iostream for memory
|
||||
efficiency
|
||||
*/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <string>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include "Base64.h"
|
||||
|
||||
// clazy:excludeall=non-pod-global-static
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
|
||||
static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
const signed char* Base::base64_decode_table()
|
||||
{
|
||||
static const signed char _table[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -1, -2, -1, -1, // 0-15
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
|
||||
-2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, // 48-63
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255
|
||||
};
|
||||
return _table;
|
||||
}
|
||||
|
||||
std::string Base::base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
std::size_t Base::base64_encode(char* out, void const* in, std::size_t in_len)
|
||||
{
|
||||
char* ret = out;
|
||||
unsigned char const* bytes_to_encode = reinterpret_cast<unsigned char const*>(in);
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if (i == 3) {
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(i = 0; (i <4) ; i++)
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
i = 0;
|
||||
for (i = 0; (i < 4); i++) {
|
||||
*ret++ = base64_chars[char_array_4[i]];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
if (i) {
|
||||
for (j = i; j < 3; j++) {
|
||||
char_array_3[j] = '\0';
|
||||
}
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++)
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
for (j = 0; (j < i + 1); j++) {
|
||||
*ret++ = base64_chars[char_array_4[j]];
|
||||
}
|
||||
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
std::string Base::base64_decode(std::string const& encoded_string) {
|
||||
int in_len = encoded_string.size();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
std::string ret;
|
||||
|
||||
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||
if (i ==4) {
|
||||
for (i = 0; i <4; i++)
|
||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++)
|
||||
ret += char_array_3[i];
|
||||
i = 0;
|
||||
while ((i++ < 3)) {
|
||||
*ret++ = '=';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j <4; j++)
|
||||
char_array_4[j] = 0;
|
||||
|
||||
for (j = 0; j <4; j++)
|
||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret - out;
|
||||
}
|
||||
|
||||
std::pair<std::size_t, std::size_t>
|
||||
Base::base64_decode(void* _out, char const* in, std::size_t in_len)
|
||||
{
|
||||
unsigned char* out = reinterpret_cast<unsigned char*>(_out);
|
||||
unsigned char* ret = out;
|
||||
char const* input = in;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_4[4], char_array_3[3];
|
||||
|
||||
static const signed char* table = base64_decode_table();
|
||||
|
||||
while (in_len-- && *in != '=') {
|
||||
const signed char v = table[(int)(*in++)];
|
||||
if (v < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
char_array_4[i++] = (unsigned char)v;
|
||||
if (i == 4) {
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++) {
|
||||
*ret++ = char_array_3[i];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
for (j = i; j < 4; j++) {
|
||||
char_array_4[j] = 0;
|
||||
}
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++) {
|
||||
*ret++ = char_array_3[j];
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair((std::size_t)(ret - out), (std::size_t)(in - input));
|
||||
}
|
||||
|
||||
@@ -1,42 +1,439 @@
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
Copyright (C) 2019 Zheng Lei (realthunder.dev@gmail.com)
|
||||
|
||||
* Added support of in-line transformation using boost iostream for memory
|
||||
efficiency
|
||||
*/
|
||||
#ifndef BASE_BASE64_H
|
||||
#define BASE_BASE64_H
|
||||
|
||||
#include <FCGlobal.h>
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/filtering_stream.hpp>
|
||||
#include <boost/iostreams/operations.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
std::string BaseExport base64_encode(unsigned char const* , unsigned int len);
|
||||
std::string BaseExport base64_decode(std::string const& s);
|
||||
enum class Base64ErrorHandling
|
||||
{
|
||||
throws,
|
||||
silent
|
||||
};
|
||||
|
||||
/// Returns the max bytes of a encoded base64 string
|
||||
inline std::size_t base64_encode_size(std::size_t len)
|
||||
{
|
||||
return 4 * ((len + 2) / 3);
|
||||
}
|
||||
|
||||
#endif
|
||||
/// Returns the max bytes of a decoded base64 binary string
|
||||
inline std::size_t base64_decode_size(std::size_t len)
|
||||
{
|
||||
return len / 4 * 3;
|
||||
}
|
||||
|
||||
/** Encode input binary with base64
|
||||
* @param out: output buffer with minimum size of base64_encode(len)
|
||||
* appending new data.
|
||||
* @param in: input binary data
|
||||
* @param len: input length
|
||||
* @return The character count written to output.
|
||||
*/
|
||||
BaseExport std::size_t base64_encode(char* out, void const* in, std::size_t len);
|
||||
|
||||
/** Return the internal base64 decoding table
|
||||
*
|
||||
* The table maps from any 8-bit character to the decoded binary bits.
|
||||
* Valid base64 characters are mapped to the corresponding 6-bit binary
|
||||
* data. White space (space, tab, vtab, CR and LF) characters are mapped
|
||||
* to -2. Other invalid characters are mapped to -1.
|
||||
*/
|
||||
BaseExport const signed char* base64_decode_table();
|
||||
|
||||
/** Decode the input base64 string into binary data
|
||||
* @param out: output buffer with minimum size of base64_encode(len)
|
||||
* appending new data.
|
||||
* @param in: input binary data
|
||||
* @param len: input length
|
||||
* @return Return a pair of output size and input read size. Compare the
|
||||
* read size to input size to check for error.
|
||||
*/
|
||||
BaseExport std::pair<std::size_t, std::size_t>
|
||||
base64_decode(void* out, char const*, std::size_t len);
|
||||
|
||||
/** Encode input binary into base64 string
|
||||
* @param out: output string. Note that the string is not cleared before
|
||||
* adding new content.
|
||||
* @param in: input binary data
|
||||
* @param len: input length
|
||||
*/
|
||||
inline void base64_encode(std::string& out, void const* in, std::size_t len)
|
||||
{
|
||||
std::size_t size = out.size();
|
||||
out.resize(size + base64_encode_size(len));
|
||||
len = base64_encode(&out[size], in, len);
|
||||
out.resize(size + len);
|
||||
}
|
||||
|
||||
/** Encode input binary into base64 string
|
||||
* @param in: input binary data
|
||||
* @param len: input length
|
||||
* @return Return the base64 string.
|
||||
*/
|
||||
inline std::string base64_encode(void const* in, std::size_t len)
|
||||
{
|
||||
std::string out;
|
||||
base64_encode(out, in, len);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Decode base64 string into binary data
|
||||
* @param out: output binary data. Note that the data is not cleared before
|
||||
* adding new content.
|
||||
* @param in: input base64 string
|
||||
* @param len: input length
|
||||
* @return Return the processed input length. Compare this with the
|
||||
* argument \c len to check for error.
|
||||
*/
|
||||
template<typename T>
|
||||
inline std::size_t base64_decode(T& out, char const* in, std::size_t len)
|
||||
{
|
||||
std::size_t size = out.size();
|
||||
out.resize(size + base64_decode_size(len));
|
||||
std::pair<std::size_t, std::size_t> res = base64_decode(&out[size], in, len);
|
||||
out.resize(size + res.first);
|
||||
return res.second;
|
||||
}
|
||||
|
||||
/** Decode base64 string into binary data
|
||||
* @param out: output binary data. Note that the data is not cleared before
|
||||
* adding new content.
|
||||
* @param s: input base64 string
|
||||
* @return Return the processed input length. Compare this with the
|
||||
* argument \c len to check for error.
|
||||
*/
|
||||
template<typename T>
|
||||
inline std::size_t base64_decode(T& out, std::string const& s)
|
||||
{
|
||||
return base64_decode(out, s.c_str(), s.size());
|
||||
}
|
||||
|
||||
/** Decode base64 string into binary data
|
||||
* @param out adding new content.
|
||||
* @param s: input base64 string
|
||||
* @return Return the decoded binary data.
|
||||
*/
|
||||
inline std::string base64_decode(std::string const& s)
|
||||
{
|
||||
std::string out;
|
||||
base64_decode(out, s.c_str(), s.size());
|
||||
return out;
|
||||
}
|
||||
|
||||
namespace bio = boost::iostreams;
|
||||
|
||||
/** A base64 encoder that can be used as a boost iostream filter
|
||||
*
|
||||
* @sa See create_base64_encoder() for example usage
|
||||
*/
|
||||
struct base64_encoder
|
||||
{
|
||||
|
||||
typedef char char_type;
|
||||
struct category: bio::multichar_output_filter_tag,
|
||||
bio::closable_tag,
|
||||
bio::optimally_buffered_tag
|
||||
{
|
||||
};
|
||||
|
||||
/** Constructor
|
||||
* @param line_size: line size for the output base64 string, 0 to
|
||||
* disable segmentation.
|
||||
*/
|
||||
base64_encoder(std::size_t line_size)
|
||||
: line_size(line_size)
|
||||
{}
|
||||
|
||||
std::streamsize optimal_buffer_size() const
|
||||
{
|
||||
return base64_decode_size(line_size);
|
||||
}
|
||||
|
||||
template<typename Device>
|
||||
void close(Device& dev)
|
||||
{
|
||||
if (pending_size) {
|
||||
base64_encode(buffer, pending, pending_size);
|
||||
}
|
||||
if (buffer.size()) {
|
||||
bio::write(dev, buffer.c_str(), buffer.size());
|
||||
if (line_size) {
|
||||
bio::put(dev, '\n');
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
else if (pos && line_size) {
|
||||
bio::put(dev, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Device>
|
||||
std::streamsize write(Device& dev, const char_type* s, std::streamsize n)
|
||||
{
|
||||
std::streamsize res = n;
|
||||
|
||||
if (pending_size) {
|
||||
while (n && pending_size < 3) {
|
||||
pending[pending_size++] = *s++;
|
||||
--n;
|
||||
}
|
||||
if (pending_size != 3) {
|
||||
return res;
|
||||
}
|
||||
|
||||
base64_encode(buffer, pending, 3);
|
||||
}
|
||||
pending_size = n % 3;
|
||||
n = n / 3 * 3;
|
||||
base64_encode(buffer, s, n);
|
||||
s += n;
|
||||
for (unsigned i = 0; i < pending_size; ++i) {
|
||||
pending[i] = s[i];
|
||||
}
|
||||
|
||||
const char* buf = buffer.c_str();
|
||||
const char* end = buf + buffer.size();
|
||||
if (line_size && buffer.size() >= line_size - pos) {
|
||||
bio::write(dev, buf, line_size - pos);
|
||||
bio::put(dev, '\n');
|
||||
buf += line_size - pos;
|
||||
pos = 0;
|
||||
for (; end - buf >= (int)line_size; buf += line_size) {
|
||||
bio::write(dev, buf, line_size);
|
||||
bio::put(dev, '\n');
|
||||
}
|
||||
}
|
||||
pos += end - buf;
|
||||
bio::write(dev, buf, end - buf);
|
||||
buffer.clear();
|
||||
return n;
|
||||
}
|
||||
|
||||
std::size_t line_size;
|
||||
std::size_t pos = 0;
|
||||
std::size_t pending_size = 0;
|
||||
unsigned char pending[3];
|
||||
std::string buffer;
|
||||
};
|
||||
|
||||
/** A base64 decoder that can be used as a boost iostream filter
|
||||
*
|
||||
* @sa See create_base64_decoder() for example usage
|
||||
*/
|
||||
struct base64_decoder
|
||||
{
|
||||
|
||||
typedef char char_type;
|
||||
struct category: bio::multichar_input_filter_tag, bio::optimally_buffered_tag
|
||||
{
|
||||
};
|
||||
|
||||
/** Constructor
|
||||
* @param line_size: line size of the encoded base64 string. This is
|
||||
* used just as a suggestion for better buffering.
|
||||
* @param silent: whether to throw on invalid non white space character.
|
||||
*/
|
||||
base64_decoder(std::size_t line_size, Base64ErrorHandling errHandling)
|
||||
: line_size(line_size)
|
||||
, errHandling(errHandling)
|
||||
{}
|
||||
|
||||
std::streamsize optimal_buffer_size() const
|
||||
{
|
||||
return base64_encode_size(line_size != 0U ? line_size : 1024);
|
||||
}
|
||||
|
||||
template<typename Device>
|
||||
std::streamsize read(Device& dev, char_type* s, std::streamsize n)
|
||||
{
|
||||
static const signed char* table = base64_decode_table();
|
||||
|
||||
if (!n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::streamsize count = 0;
|
||||
|
||||
for (;;) {
|
||||
while (pending_out < out_count) {
|
||||
*s++ = char_array_3[pending_out++];
|
||||
++count;
|
||||
if (--n == 0) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
if (eof) {
|
||||
return count ? count : -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int d = bio::get(dev);
|
||||
if (d < 0) {
|
||||
eof = true;
|
||||
if (pending_in <= 1) {
|
||||
if (pending_in == 1 && errHandling == Base64ErrorHandling::throws) {
|
||||
throw BOOST_IOSTREAMS_FAILURE("Unexpected ending of base64 string");
|
||||
}
|
||||
return count ? count : -1;
|
||||
}
|
||||
out_count = pending_in - 1;
|
||||
pending_in = 4;
|
||||
}
|
||||
else {
|
||||
signed char c = table[d];
|
||||
if (c < 0) {
|
||||
if (c == -2 || errHandling == Base64ErrorHandling::silent) {
|
||||
continue;
|
||||
}
|
||||
throw BOOST_IOSTREAMS_FAILURE("Invalid character in base64 string");
|
||||
}
|
||||
char_array_4[pending_in++] = (char)c;
|
||||
}
|
||||
if (pending_in == 4) {
|
||||
pending_out = pending_in = 0;
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] =
|
||||
((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t line_size;
|
||||
std::uint8_t pending_in = 0;
|
||||
char char_array_4[4];
|
||||
std::uint8_t pending_out = 3;
|
||||
std::uint8_t out_count = 3;
|
||||
char char_array_3[3];
|
||||
Base64ErrorHandling errHandling;
|
||||
bool eof = false;
|
||||
};
|
||||
|
||||
/** Create an output stream that transforms the input binary data to base64 strings
|
||||
*
|
||||
* @param out: the downstream output stream that will be fed with base64 string
|
||||
* @param line_size: line size of the base64 string. Zero to disable segmenting.
|
||||
*
|
||||
* @return A unique pointer to an output stream that can transforms the
|
||||
* input binary data to base64 strings.
|
||||
*/
|
||||
inline std::unique_ptr<std::ostream> create_base64_encoder(std::ostream& out,
|
||||
std::size_t line_size = 80)
|
||||
{
|
||||
std::unique_ptr<std::ostream> res(new bio::filtering_ostream);
|
||||
auto* f = static_cast<bio::filtering_ostream*>(res.get());
|
||||
f->push(base64_encoder(line_size));
|
||||
f->push(out);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Create an output stream that stores the input binary data to file as base64 strings
|
||||
*
|
||||
* @param filename: the output file path
|
||||
* @param line_size: line size of the base64 string. Zero to disable segmenting.
|
||||
*
|
||||
* @return A unique pointer to an output stream that can transforms the
|
||||
* input binary data to base64 strings.
|
||||
*/
|
||||
inline std::unique_ptr<std::ostream> create_base64_encoder(const std::string& filepath,
|
||||
std::size_t line_size = 80)
|
||||
{
|
||||
std::unique_ptr<std::ostream> res(new bio::filtering_ostream);
|
||||
bio::filtering_ostream* f = static_cast<bio::filtering_ostream*>(res.get());
|
||||
f->push(base64_encoder(line_size));
|
||||
f->push(bio::file_sink(filepath));
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Create an input stream that can transform base64 into binary
|
||||
*
|
||||
* @param in: input upstream.
|
||||
* @param line_size: line size of the encoded base64 string. This is
|
||||
* used just as a suggestion for better buffering.
|
||||
* @param silent: whether to throw on invalid non white space character.
|
||||
*
|
||||
* @return A unique pointer to an input stream that read from the given
|
||||
* upstream and transform the read base64 strings into binary data.
|
||||
*/
|
||||
inline std::unique_ptr<std::istream>
|
||||
create_base64_decoder(std::istream& in,
|
||||
std::size_t line_size = 80,
|
||||
Base64ErrorHandling errHandling = Base64ErrorHandling::silent)
|
||||
{
|
||||
std::unique_ptr<std::istream> res(new bio::filtering_istream);
|
||||
bio::filtering_istream* f = static_cast<bio::filtering_istream*>(res.get());
|
||||
f->push(base64_decoder(line_size, errHandling));
|
||||
f->push(in);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Create an input stream that can transform base64 into binary
|
||||
*
|
||||
* @param filepath: input file.
|
||||
* @param ending: optional ending character. If non zero, the filter
|
||||
* will signal EOF when encounter this character.
|
||||
* @param putback: if true and the filter read the ending character
|
||||
* it will put it back into upstream
|
||||
* @param line_size: line size of the encoded base64 string. This is
|
||||
* used just as a suggestion for better buffering.
|
||||
* @param silent: whether to throw on invalid non white space character.
|
||||
*
|
||||
* @return A unique pointer to an input stream that read from the given
|
||||
* file and transform the read base64 strings into binary data.
|
||||
*/
|
||||
inline std::unique_ptr<std::istream>
|
||||
create_base64_decoder(const std::string& filepath,
|
||||
std::size_t line_size = 80,
|
||||
Base64ErrorHandling errHandling = Base64ErrorHandling::silent)
|
||||
{
|
||||
std::unique_ptr<std::istream> res(new bio::filtering_istream);
|
||||
auto* f = static_cast<bio::filtering_istream*>(res.get());
|
||||
f->push(base64_decoder(line_size, errHandling));
|
||||
f->push(bio::file_source(filepath));
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace Base
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,15 @@
|
||||
namespace Base
|
||||
{
|
||||
|
||||
/// When reading and writing a character stream, the incoming data can be dumped into the stream
|
||||
/// unaltered (if it contains only data that is valid in the current XML character set), or it can
|
||||
/// be Base64-encoded. This enum is used by Reader and Writer to distinguish the two cases.
|
||||
enum class CharStreamFormat {
|
||||
Raw,
|
||||
Base64Encoded
|
||||
};
|
||||
static constexpr int base64LineWidth {80};
|
||||
|
||||
/** File name unification
|
||||
* This class handles everything related to file names
|
||||
* the file names are internal generally UTF-8 encoded on
|
||||
|
||||
@@ -283,8 +283,17 @@ void Base::XMLReader::readEndElement(const char* ElementName, int level)
|
||||
|| (level>=0 && level!=Level))));
|
||||
}
|
||||
|
||||
void Base::XMLReader::readCharacters()
|
||||
void Base::XMLReader::readCharacters(const char* filename, CharStreamFormat format)
|
||||
{
|
||||
Base::FileInfo fi(filename);
|
||||
Base::ofstream to(fi, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
if (!to) {
|
||||
throw Base::FileException("XMLReader::readCharacters() Could not open file!");
|
||||
}
|
||||
|
||||
beginCharStream(format) >> to.rdbuf();
|
||||
to.close();
|
||||
endCharStream();
|
||||
}
|
||||
|
||||
std::streamsize Base::XMLReader::read(char_type* s, std::streamsize n)
|
||||
@@ -336,7 +345,7 @@ std::istream& Base::XMLReader::charStream()
|
||||
return *CharStream;
|
||||
}
|
||||
|
||||
std::istream& Base::XMLReader::beginCharStream()
|
||||
std::istream& Base::XMLReader::beginCharStream(CharStreamFormat format)
|
||||
{
|
||||
if (CharStream) {
|
||||
throw Base::XMLParseException("recursive character stream");
|
||||
@@ -362,6 +371,9 @@ std::istream& Base::XMLReader::beginCharStream()
|
||||
|
||||
CharStream = std::make_unique<boost::iostreams::filtering_istream>();
|
||||
auto* filteringStream = dynamic_cast<boost::iostreams::filtering_istream*>(CharStream.get());
|
||||
if(format == CharStreamFormat::Base64Encoded) {
|
||||
filteringStream->push(base64_decoder(Base::base64LineWidth, Base64ErrorHandling::silent));
|
||||
}
|
||||
filteringStream->push(boost::ref(*this));
|
||||
return *CharStream;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
*/
|
||||
void readEndElement(const char* ElementName=nullptr, int level=-1);
|
||||
/// read until characters are found
|
||||
void readCharacters();
|
||||
void readCharacters(const char* filename, CharStreamFormat format = CharStreamFormat::Raw);
|
||||
|
||||
/** Obtain an input stream for reading characters
|
||||
*
|
||||
@@ -183,7 +183,7 @@ public:
|
||||
* auto destroyed when you call with readElement() or readEndElement(), or
|
||||
* you can end it explicitly with endCharStream().
|
||||
*/
|
||||
std::istream &beginCharStream();
|
||||
std::istream &beginCharStream(CharStreamFormat format = CharStreamFormat::Raw);
|
||||
/// Manually end the current character stream
|
||||
void endCharStream();
|
||||
/// Obtain the current character stream
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
unsigned int getAttributeCount() const;
|
||||
/// check if the read element has a special attribute
|
||||
bool hasAttribute(const char* AttrName) const;
|
||||
/// return the named attribute as an interer (does type checking)
|
||||
/// return the named attribute as an integer (does type checking)
|
||||
long getAttributeAsInteger(const char* AttrName) const;
|
||||
unsigned long getAttributeAsUnsigned(const char* AttrName) const;
|
||||
/// return the named attribute as a double floating point (does type checking)
|
||||
|
||||
@@ -83,18 +83,22 @@ Writer::Writer()
|
||||
|
||||
Writer::~Writer() = default;
|
||||
|
||||
std::ostream& Writer::beginCharStream()
|
||||
std::ostream& Writer::beginCharStream(CharStreamFormat format)
|
||||
{
|
||||
if (CharStream) {
|
||||
throw Base::RuntimeError("Writer::beginCharStream(): invalid state");
|
||||
}
|
||||
|
||||
Stream() << "<![CDATA[";
|
||||
CharStream = std::make_unique<boost::iostreams::filtering_ostream>();
|
||||
auto* filteredStream = dynamic_cast<boost::iostreams::filtering_ostream*>(CharStream.get());
|
||||
filteredStream->push(cdata_filter());
|
||||
filteredStream->push(Stream());
|
||||
*filteredStream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
|
||||
charStreamFormat = format;
|
||||
if(format == CharStreamFormat::Base64Encoded) {
|
||||
CharStream = create_base64_encoder(Stream(), Base::base64LineWidth);
|
||||
} else {
|
||||
Stream() << "<![CDATA[";
|
||||
CharStream = std::make_unique<boost::iostreams::filtering_ostream>();
|
||||
auto* filteredStream = dynamic_cast<boost::iostreams::filtering_ostream*>(CharStream.get());
|
||||
filteredStream->push(cdata_filter());
|
||||
filteredStream->push(Stream());
|
||||
*filteredStream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
|
||||
}
|
||||
return *CharStream;
|
||||
}
|
||||
|
||||
@@ -102,7 +106,9 @@ std::ostream& Writer::endCharStream()
|
||||
{
|
||||
if (CharStream) {
|
||||
CharStream.reset();
|
||||
Stream() << "]]>";
|
||||
if (charStreamFormat == CharStreamFormat::Raw) {
|
||||
Stream() << "]]>";
|
||||
}
|
||||
}
|
||||
return Stream();
|
||||
}
|
||||
|
||||
@@ -123,11 +123,17 @@ public:
|
||||
* the current XML encoding, and will be enclosed inside
|
||||
* CDATA section. The stream will scan the input and
|
||||
* properly escape any CDATA ending inside.
|
||||
*
|
||||
* @param format: If Base64Encoded, the input will be base64 encoded before storing.
|
||||
* If Raw, the input is assumed to be valid character with
|
||||
* the current XML encoding, and will be enclosed inside
|
||||
* CDATA section. The stream will scan the input and
|
||||
* properly escape any CDATA ending inside.
|
||||
* @return Returns an output stream.
|
||||
*
|
||||
* You must call endCharStream() to end the current character stream.
|
||||
*/
|
||||
std::ostream &beginCharStream();
|
||||
std::ostream& beginCharStream(CharStreamFormat format = CharStreamFormat::Raw);
|
||||
/** End the current character output stream
|
||||
* @return Returns the normal writer stream for convenience
|
||||
*/
|
||||
@@ -161,6 +167,7 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<std::ostream> CharStream;
|
||||
CharStreamFormat charStreamFormat;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -265,3 +265,21 @@ TEST_F(ReaderTest, readNextStartEndElement)
|
||||
EXPECT_STREQ(Reader()->localName(), "node2");
|
||||
EXPECT_STREQ(Reader()->getAttribute("attr"), "2");
|
||||
}
|
||||
|
||||
TEST_F(ReaderTest, charStreamBase64Encoded)
|
||||
{
|
||||
// Arrange
|
||||
static constexpr size_t bufferSize {100};
|
||||
std::array<char, bufferSize> buffer {};
|
||||
givenDataAsXMLStream("<data>RnJlZUNBRCByb2NrcyEg8J+qqPCfqqjwn6qo\n</data>");
|
||||
Reader()->readElement("data");
|
||||
Reader()->beginCharStream(Base::CharStreamFormat::Base64Encoded);
|
||||
|
||||
// Act
|
||||
Reader()->charStream().getline(buffer.data(), bufferSize);
|
||||
Reader()->endCharStream();
|
||||
|
||||
// Assert
|
||||
// Conversion done using https://www.base64encode.org for testing purposes
|
||||
EXPECT_EQ(std::string("FreeCAD rocks! 🪨🪨🪨"), std::string(buffer.data()));
|
||||
}
|
||||
|
||||
@@ -114,3 +114,18 @@ TEST_F(WriterTest, charStream)
|
||||
// Assert
|
||||
EXPECT_EQ(&streamA, &streamB);
|
||||
}
|
||||
|
||||
TEST_F(WriterTest, charStreamBase64Encoded)
|
||||
{
|
||||
// Arrange
|
||||
auto& stream {_writer.beginCharStream(Base::CharStreamFormat::Base64Encoded)};
|
||||
std::string data {"FreeCAD rocks! 🪨🪨🪨"};
|
||||
|
||||
// Act
|
||||
_writer.charStream() << data;
|
||||
_writer.endCharStream();
|
||||
|
||||
// Assert
|
||||
// Conversion done using https://www.base64encode.org for testing purposes
|
||||
EXPECT_EQ(std::string("RnJlZUNBRCByb2NrcyEg8J+qqPCfqqjwn6qo\n"), _writer.getString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user