Base/Toponaming: Refactor to extract filters to new file
This commit is contained in:
@@ -23,9 +23,9 @@ freely, subject to the following restrictions:
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
|
||||
Additional code Copyright (C) 2019 Zheng Lei (realthunder.dev@gmail.com)
|
||||
* Added support of in-line transformation using boost iostream for memory efficiency
|
||||
NOTICE: The source code here has been altered from the original to use a provided character buffer
|
||||
rather than returning a new string for each call.
|
||||
These modifications are Copyright (c) 2019 Zheng Lei (realthunder.dev@gmail.com)
|
||||
|
||||
*/
|
||||
#include "PreCompiled.h"
|
||||
@@ -36,7 +36,9 @@ Additional code Copyright (C) 2019 Zheng Lei (realthunder.dev@gmail.com)
|
||||
|
||||
#include "Base64.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
// cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers,
|
||||
// readability-magic-numbers)
|
||||
|
||||
static const std::array<char, 65> base64_chars {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
@@ -69,11 +71,11 @@ std::array<const signed char, Base::base64DecodeTableSize> Base::base64_decode_t
|
||||
std::size_t Base::base64_encode(char* out, void const* in, std::size_t in_len)
|
||||
{
|
||||
char* ret = out;
|
||||
auto const* bytes_to_encode = reinterpret_cast<unsigned char const*>(in); // NOLINT
|
||||
auto const* bytes_to_encode = reinterpret_cast<unsigned char const*>(in); // NOLINT
|
||||
int char3 {0};
|
||||
int char4 {};
|
||||
std::array<unsigned char,3> char_array_3{};
|
||||
std::array<unsigned char,4> char_array_4{};
|
||||
std::array<unsigned char, 3> char_array_3 {};
|
||||
std::array<unsigned char, 4> char_array_4 {};
|
||||
|
||||
while ((in_len--) != 0U) {
|
||||
char_array_3[char3++] = *(bytes_to_encode++);
|
||||
@@ -115,13 +117,13 @@ std::size_t Base::base64_encode(char* out, void const* in, std::size_t in_len)
|
||||
std::pair<std::size_t, std::size_t>
|
||||
Base::base64_decode(void* _out, char const* in, std::size_t in_len)
|
||||
{
|
||||
auto* out = reinterpret_cast<unsigned char*>(_out); // NOLINT
|
||||
auto* out = reinterpret_cast<unsigned char*>(_out); // NOLINT
|
||||
unsigned char* ret = out;
|
||||
char const* input = in;
|
||||
int byteCounter1 {0};
|
||||
int byteCounter2 {};
|
||||
std::array<unsigned char, 4> char_array_4{};
|
||||
std::array<unsigned char, 3> char_array_3{};
|
||||
std::array<unsigned char, 4> char_array_4 {};
|
||||
std::array<unsigned char, 3> char_array_3 {};
|
||||
|
||||
static auto table = base64_decode_table();
|
||||
|
||||
@@ -161,4 +163,6 @@ Base::base64_decode(void* _out, char const* in, std::size_t in_len)
|
||||
return std::make_pair((std::size_t)(ret - out), (std::size_t)(in - input));
|
||||
}
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
// cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers,
|
||||
// readability-magic-numbers)
|
||||
|
||||
@@ -23,36 +23,28 @@ freely, subject to the following restrictions:
|
||||
|
||||
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
|
||||
NOTICE: The source code here has been altered from the original to use a provided character buffer
|
||||
rather than returning a new string for each call.
|
||||
These modifications are Copyright (c) 2019 Zheng Lei (realthunder.dev@gmail.com)
|
||||
*/
|
||||
#ifndef BASE_BASE64_H
|
||||
#define BASE_BASE64_H
|
||||
|
||||
#include <array>
|
||||
#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>
|
||||
|
||||
#include "FCGlobal.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
// cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers,
|
||||
// readability-magic-numbers)
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
enum class Base64ErrorHandling
|
||||
{
|
||||
throws,
|
||||
silent
|
||||
};
|
||||
static constexpr int base64DefaultBufferSize {80};
|
||||
static constexpr size_t base64DecodeTableSize {256};
|
||||
|
||||
/// Returns the max bytes of a encoded base64 string
|
||||
@@ -165,289 +157,10 @@ inline std::string base64_decode(std::string const& str)
|
||||
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
|
||||
{
|
||||
|
||||
using char_type = char;
|
||||
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 static_cast<std::streamsize>(base64_decode_size(line_size));
|
||||
}
|
||||
|
||||
template<typename Device>
|
||||
void close(Device& dev)
|
||||
{
|
||||
if (pending_size) {
|
||||
base64_encode(buffer, pending.data(), pending_size);
|
||||
}
|
||||
if (!buffer.empty()) {
|
||||
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* str, std::streamsize n)
|
||||
{
|
||||
std::streamsize res = n;
|
||||
|
||||
if (pending_size > 0) {
|
||||
while (n && pending_size < 3) {
|
||||
pending[pending_size] = *str++;
|
||||
++pending_size;
|
||||
--n;
|
||||
}
|
||||
if (pending_size != 3) {
|
||||
return res;
|
||||
}
|
||||
|
||||
base64_encode(buffer, pending.data(), 3);
|
||||
}
|
||||
pending_size = n % 3;
|
||||
n = n / 3 * 3;
|
||||
base64_encode(buffer, str, n);
|
||||
str += n;
|
||||
for (unsigned i = 0; i < pending_size; ++i) {
|
||||
pending[i] = str[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;
|
||||
std::array<unsigned char, 3> pending {};
|
||||
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
|
||||
{
|
||||
|
||||
using char_type = char;
|
||||
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
|
||||
{
|
||||
static constexpr int defaultBufferSize {1024};
|
||||
return static_cast<std::streamsize>(
|
||||
base64_encode_size(line_size != 0U ? line_size : defaultBufferSize));
|
||||
}
|
||||
|
||||
template<typename Device>
|
||||
std::streamsize read(Device& dev, char_type* str, std::streamsize n)
|
||||
{
|
||||
static auto table = base64_decode_table();
|
||||
|
||||
if (!n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::streamsize count = 0;
|
||||
|
||||
for (;;) {
|
||||
while (pending_out < out_count) {
|
||||
*str++ = char_array_3[pending_out++];
|
||||
++count;
|
||||
if (--n == 0) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
if (eof) {
|
||||
return count ? count : -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int newChar = bio::get(dev);
|
||||
if (newChar < 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 decodedChar = table[newChar];
|
||||
if (decodedChar < 0) {
|
||||
if (decodedChar == -2 || errHandling == Base64ErrorHandling::silent) {
|
||||
continue;
|
||||
}
|
||||
throw BOOST_IOSTREAMS_FAILURE("Invalid character in base64 string");
|
||||
}
|
||||
char_array_4[pending_in++] = (char)decodedChar;
|
||||
}
|
||||
if (pending_in == 4) {
|
||||
pending_out = pending_in = 0;
|
||||
char_array_3[0] =
|
||||
static_cast<char>((char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4));
|
||||
char_array_3[1] = static_cast<char>(((char_array_4[1] & 0xf) << 4)
|
||||
+ ((char_array_4[2] & 0x3c) >> 2));
|
||||
char_array_3[2] =
|
||||
static_cast<char>(((char_array_4[2] & 0x3) << 6) + char_array_4[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t line_size;
|
||||
std::uint8_t pending_in = 0;
|
||||
std::array<char, 4> char_array_4 {};
|
||||
std::uint8_t pending_out = 3;
|
||||
std::uint8_t out_count = 3;
|
||||
std::array<char, 3> char_array_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 = base64DefaultBufferSize)
|
||||
{
|
||||
std::unique_ptr<std::ostream> res(new bio::filtering_ostream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_ostream*>(res.get());
|
||||
filteringStream->push(base64_encoder(line_size));
|
||||
filteringStream->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 = base64DefaultBufferSize)
|
||||
{
|
||||
std::unique_ptr<std::ostream> res(new bio::filtering_ostream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_ostream*>(res.get());
|
||||
filteringStream->push(base64_encoder(line_size));
|
||||
filteringStream->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 = base64DefaultBufferSize,
|
||||
Base64ErrorHandling errHandling = Base64ErrorHandling::silent)
|
||||
{
|
||||
std::unique_ptr<std::istream> res(new bio::filtering_istream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_istream*>(res.get());
|
||||
filteringStream->push(base64_decoder(line_size, errHandling));
|
||||
filteringStream->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 = base64DefaultBufferSize,
|
||||
Base64ErrorHandling errHandling = Base64ErrorHandling::silent)
|
||||
{
|
||||
std::unique_ptr<std::istream> res(new bio::filtering_istream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_istream*>(res.get());
|
||||
filteringStream->push(base64_decoder(line_size, errHandling));
|
||||
filteringStream->push(bio::file_source(filepath));
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace Base
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
// cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers,
|
||||
// readability-magic-numbers)
|
||||
|
||||
#endif
|
||||
|
||||
340
src/Base/Base64Filter.h
Normal file
340
src/Base/Base64Filter.h
Normal file
@@ -0,0 +1,340 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2019 Zheng Lei (realthunder.dev@gmail.com) *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef FREECAD_BASE_BASE64FILTER_H
|
||||
#define FREECAD_BASE_BASE64FILTER_H
|
||||
|
||||
|
||||
#include "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>
|
||||
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
// cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers,
|
||||
// readability-magic-numbers)
|
||||
|
||||
namespace Base
|
||||
{
|
||||
|
||||
namespace bio = boost::iostreams;
|
||||
|
||||
enum class Base64ErrorHandling
|
||||
{
|
||||
throws,
|
||||
silent
|
||||
};
|
||||
static constexpr int base64DefaultBufferSize {80};
|
||||
|
||||
/** A base64 encoder that can be used as a boost iostream filter
|
||||
*
|
||||
* @sa See create_base64_encoder() for example usage
|
||||
*/
|
||||
struct base64_encoder
|
||||
{
|
||||
|
||||
using char_type = char;
|
||||
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.
|
||||
*/
|
||||
explicit base64_encoder(std::size_t line_size)
|
||||
: line_size(line_size)
|
||||
{}
|
||||
|
||||
std::streamsize optimal_buffer_size() const
|
||||
{
|
||||
static constexpr int defaultBufferSize {1024};
|
||||
return static_cast<std::streamsize>(
|
||||
base64_encode_size(line_size != 0U ? line_size : defaultBufferSize));
|
||||
}
|
||||
|
||||
template<typename Device>
|
||||
void close(Device& dev)
|
||||
{
|
||||
if (pending_size) {
|
||||
base64_encode(buffer, pending.data(), pending_size);
|
||||
}
|
||||
if (!buffer.empty()) {
|
||||
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* str, std::streamsize n)
|
||||
{
|
||||
std::streamsize res = n;
|
||||
|
||||
if (pending_size > 0) {
|
||||
while (n && pending_size < 3) {
|
||||
pending[pending_size] = *str++;
|
||||
++pending_size;
|
||||
--n;
|
||||
}
|
||||
if (pending_size != 3) {
|
||||
return res;
|
||||
}
|
||||
|
||||
base64_encode(buffer, pending.data(), 3);
|
||||
}
|
||||
pending_size = n % 3;
|
||||
n = n / 3 * 3;
|
||||
base64_encode(buffer, str, n);
|
||||
str += n;
|
||||
for (unsigned i = 0; i < pending_size; ++i) {
|
||||
pending[i] = str[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;
|
||||
std::array<unsigned char, 3> pending {};
|
||||
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
|
||||
{
|
||||
|
||||
using char_type = char;
|
||||
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
|
||||
{
|
||||
static constexpr int defaultBufferSize {1024};
|
||||
return static_cast<std::streamsize>(
|
||||
base64_encode_size(line_size != 0U ? line_size : defaultBufferSize));
|
||||
}
|
||||
|
||||
template<typename Device>
|
||||
std::streamsize read(Device& dev, char_type* str, std::streamsize n)
|
||||
{
|
||||
static auto table = base64_decode_table();
|
||||
|
||||
if (!n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::streamsize count = 0;
|
||||
|
||||
for (;;) {
|
||||
while (pending_out < out_count) {
|
||||
*str++ = char_array_3[pending_out++];
|
||||
++count;
|
||||
if (--n == 0) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
if (eof) {
|
||||
return count ? count : -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int newChar = bio::get(dev);
|
||||
if (newChar < 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 decodedChar = table[newChar];
|
||||
if (decodedChar < 0) {
|
||||
if (decodedChar == -2 || errHandling == Base64ErrorHandling::silent) {
|
||||
continue;
|
||||
}
|
||||
throw BOOST_IOSTREAMS_FAILURE("Invalid character in base64 string");
|
||||
}
|
||||
char_array_4[pending_in++] = (char)decodedChar;
|
||||
}
|
||||
if (pending_in == 4) {
|
||||
pending_out = pending_in = 0;
|
||||
char_array_3[0] =
|
||||
static_cast<char>((char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4));
|
||||
char_array_3[1] = static_cast<char>(((char_array_4[1] & 0xf) << 4)
|
||||
+ ((char_array_4[2] & 0x3c) >> 2));
|
||||
char_array_3[2] =
|
||||
static_cast<char>(((char_array_4[2] & 0x3) << 6) + char_array_4[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t line_size;
|
||||
std::uint8_t pending_in = 0;
|
||||
std::array<char, 4> char_array_4 {};
|
||||
std::uint8_t pending_out = 3;
|
||||
std::uint8_t out_count = 3;
|
||||
std::array<char, 3> char_array_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 = base64DefaultBufferSize)
|
||||
{
|
||||
std::unique_ptr<std::ostream> res(new bio::filtering_ostream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_ostream*>(res.get());
|
||||
filteringStream->push(base64_encoder(line_size));
|
||||
filteringStream->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 = base64DefaultBufferSize)
|
||||
{
|
||||
std::unique_ptr<std::ostream> res(new bio::filtering_ostream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_ostream*>(res.get());
|
||||
filteringStream->push(base64_encoder(line_size));
|
||||
filteringStream->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 = base64DefaultBufferSize,
|
||||
Base64ErrorHandling errHandling = Base64ErrorHandling::silent)
|
||||
{
|
||||
std::unique_ptr<std::istream> res(new bio::filtering_istream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_istream*>(res.get());
|
||||
filteringStream->push(base64_decoder(line_size, errHandling));
|
||||
filteringStream->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 = base64DefaultBufferSize,
|
||||
Base64ErrorHandling errHandling = Base64ErrorHandling::silent)
|
||||
{
|
||||
std::unique_ptr<std::istream> res(new bio::filtering_istream);
|
||||
auto* filteringStream = dynamic_cast<bio::filtering_istream*>(res.get());
|
||||
filteringStream->push(base64_decoder(line_size, errHandling));
|
||||
filteringStream->push(bio::file_source(filepath));
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace Base
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
// cppcoreguidelines-pro-bounds-constant-array-index, cppcoreguidelines-avoid-magic-numbers,
|
||||
// readability-magic-numbers)
|
||||
|
||||
#endif // FREECAD_BASE_BASE64FILTER_H
|
||||
@@ -287,6 +287,7 @@ SET(SWIG_HEADERS
|
||||
SET(FreeCADBase_HPP_SRCS
|
||||
Axis.h
|
||||
Base64.h
|
||||
Base64Filter.h
|
||||
BaseClass.h
|
||||
BindingManager.h
|
||||
Bitmask.h
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "Reader.h"
|
||||
#include "Base64.h"
|
||||
#include "Base64Filter.h"
|
||||
#include "Console.h"
|
||||
#include "InputSource.h"
|
||||
#include "Persistence.h"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "Writer.h"
|
||||
#include "Base64.h"
|
||||
#include "Base64Filter.h"
|
||||
#include "Exception.h"
|
||||
#include "FileInfo.h"
|
||||
#include "Persistence.h"
|
||||
|
||||
91
tests/src/Base/Base64.cpp
Normal file
91
tests/src/Base/Base64.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
NOTICE: This test has been modified from the original code to remove output to stdout, and to split
|
||||
the tests into individual parts.
|
||||
|
||||
*/
|
||||
|
||||
#include "Base/Base64.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace Base;
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
|
||||
TEST(Base64, encode)
|
||||
{
|
||||
const std::string str = "René Nyffenegger\n"
|
||||
"http://www.renenyffenegger.ch\n"
|
||||
"passion for data\n";
|
||||
|
||||
auto encoded = base64_encode(reinterpret_cast<const unsigned char*>(str.c_str()), str.length());
|
||||
auto decoded = base64_decode(std::string(encoded));
|
||||
|
||||
ASSERT_EQ(decoded, str);
|
||||
}
|
||||
|
||||
TEST(Base64, exactlyFourBytes)
|
||||
{
|
||||
// Test all possibilities of fill bytes (none, one =, two ==)
|
||||
// References calculated with: https://www.base64encode.org/
|
||||
|
||||
std::string rest0_original = "abc";
|
||||
// std::string rest0_reference = "YWJj";
|
||||
|
||||
std::string rest0_encoded =
|
||||
base64_encode(reinterpret_cast<const unsigned char*>(rest0_original.c_str()),
|
||||
rest0_original.length());
|
||||
std::string rest0_decoded = base64_decode(rest0_encoded);
|
||||
|
||||
ASSERT_EQ(rest0_decoded, rest0_original);
|
||||
}
|
||||
|
||||
TEST(Base64, twoEqualsSignsPadding)
|
||||
{
|
||||
std::string rest1_original = "abcd";
|
||||
// std::string rest1_reference = "YWJjZA==";
|
||||
|
||||
std::string rest1_encoded =
|
||||
base64_encode(reinterpret_cast<const unsigned char*>(rest1_original.c_str()),
|
||||
rest1_original.length());
|
||||
std::string rest1_decoded = base64_decode(rest1_encoded);
|
||||
|
||||
ASSERT_EQ(rest1_decoded, rest1_original);
|
||||
}
|
||||
|
||||
TEST(Base64, oneEqualsSignPadding)
|
||||
{
|
||||
std::string rest2_original = "abcde";
|
||||
// std::string rest2_reference = "YWJjZGU=";
|
||||
|
||||
std::string rest2_encoded =
|
||||
base64_encode(reinterpret_cast<const unsigned char*>(rest2_original.c_str()),
|
||||
rest2_original.length());
|
||||
std::string rest2_decoded = base64_decode(rest2_encoded);
|
||||
|
||||
ASSERT_EQ(rest2_decoded, rest2_original);
|
||||
}
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
@@ -2,6 +2,7 @@ target_sources(
|
||||
Tests_run
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Axis.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Base64.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Bitmask.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BoundBox.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Builder3D.cpp
|
||||
|
||||
@@ -118,7 +118,7 @@ TEST_F(WriterTest, charStream)
|
||||
TEST_F(WriterTest, charStreamBase64Encoded)
|
||||
{
|
||||
// Arrange
|
||||
auto& stream {_writer.beginCharStream(Base::CharStreamFormat::Base64Encoded)};
|
||||
_writer.beginCharStream(Base::CharStreamFormat::Base64Encoded);
|
||||
std::string data {"FreeCAD rocks! 🪨🪨🪨"};
|
||||
|
||||
// Act
|
||||
|
||||
Reference in New Issue
Block a user