1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00
Files
opencv/modules/core/src/persistence_base64_encoding.cpp
T
Sean McBride 5fb3869775 Merge pull request #23109 from seanm:misc-warnings
* Fixed clang -Wnewline-eof warnings
* Fixed all trivial clang -Wextra-semi and -Wc++98-compat-extra-semi warnings
* Removed trailing semi from various macros
* Fixed various -Wunused-macros warnings
* Fixed some trivial -Wdocumentation warnings
* Fixed some -Wdocumentation-deprecated-sync warnings
* Fixed incorrect indentation
* Suppressed some clang warnings in 3rd party code
* Fixed QRCodeEncoder::Params documentation.

---------

Co-authored-by: Alexander Smorkalov <alexander.smorkalov@xperience.ai>
2023-10-06 13:33:21 +03:00

371 lines
11 KiB
C++

// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "precomp.hpp"
#include "persistence_impl.hpp"
#include "persistence_base64_encoding.hpp"
namespace cv
{
class base64::Base64ContextEmitter
{
public:
explicit Base64ContextEmitter(cv::FileStorage::Impl& fs, bool needs_indent_)
: file_storage(fs)
, needs_indent(needs_indent_)
, binary_buffer(BUFFER_LEN)
, base64_buffer(base64_encode_buffer_size(BUFFER_LEN))
, src_beg(0)
, src_cur(0)
, src_end(0)
{
src_beg = binary_buffer.data();
src_end = src_beg + BUFFER_LEN;
src_cur = src_beg;
CV_Assert(fs.write_mode);
if (needs_indent)
{
file_storage.flush();
}
}
~Base64ContextEmitter()
{
/* cleaning */
if (src_cur != src_beg)
flush(); /* encode the rest binary data to base64 buffer */
}
Base64ContextEmitter & write(const uchar * beg, const uchar * end)
{
if (beg >= end)
return *this;
while (beg < end) {
/* collect binary data and copy to binary buffer */
size_t len = std::min(end - beg, src_end - src_cur);
std::memcpy(src_cur, beg, len);
beg += len;
src_cur += len;
if (src_cur >= src_end) {
/* binary buffer is full. */
/* encode it to base64 and send result to fs */
flush();
}
}
return *this;
}
/*
* a convertor must provide :
* - `operator >> (uchar * & dst)` for writing current binary data to `dst` and moving to next data.
* - `operator bool` for checking if current loaction is valid and not the end.
*/
template<typename _to_binary_convertor_t> inline
Base64ContextEmitter & write(_to_binary_convertor_t & convertor)
{
static const size_t BUFFER_MAX_LEN = 1024U;
std::vector<uchar> buffer(BUFFER_MAX_LEN);
uchar * beg = buffer.data();
uchar * end = beg;
while (convertor) {
convertor >> end;
write(beg, end);
end = beg;
}
return *this;
}
bool flush()
{
/* control line width, so on. */
size_t len = base64_encode(src_beg, base64_buffer.data(), 0U, src_cur - src_beg);
if (len == 0U)
return false;
src_cur = src_beg;
if ( !needs_indent)
{
file_storage.puts((const char*)base64_buffer.data());
}
else
{
const char newline[] = "\n";
char space[80];
int ident = file_storage.write_stack.back().indent;
memset(space, ' ', static_cast<int>(ident));
space[ident] = '\0';
file_storage.puts(space);
file_storage.puts((const char*)base64_buffer.data());
file_storage.puts(newline);
file_storage.flush();
}
return true;
}
private:
/* because of Base64, we must keep its length a multiple of 3 */
static const size_t BUFFER_LEN = 48U;
// static_assert(BUFFER_LEN % 3 == 0, "BUFFER_LEN is invalid");
private:
cv::FileStorage::Impl& file_storage;
bool needs_indent;
std::vector<uchar> binary_buffer;
std::vector<uchar> base64_buffer;
uchar * src_beg;
uchar * src_cur;
uchar * src_end;
};
std::string base64::make_base64_header(const char *dt) {
std::ostringstream oss;
oss << dt << ' ';
std::string buffer(oss.str());
CV_Assert(buffer.size() < ::base64::HEADER_SIZE);
buffer.reserve(::base64::HEADER_SIZE);
while (buffer.size() < ::base64::HEADER_SIZE)
buffer += ' ';
return buffer;
}
size_t base64::base64_encode(const uint8_t *src, uint8_t *dst, size_t off, size_t cnt) {
if (!src || !dst || !cnt)
return 0;
/* initialize beginning and end */
uint8_t * dst_beg = dst;
uint8_t * dst_cur = dst_beg;
uint8_t const * src_beg = src + off;
uint8_t const * src_cur = src_beg;
uint8_t const * src_end = src_cur + cnt / 3U * 3U;
/* integer multiples part */
while (src_cur < src_end) {
uint8_t _2 = *src_cur++;
uint8_t _1 = *src_cur++;
uint8_t _0 = *src_cur++;
*dst_cur++ = base64_mapping[ _2 >> 2U];
*dst_cur++ = base64_mapping[(_1 & 0xF0U) >> 4U | (_2 & 0x03U) << 4U];
*dst_cur++ = base64_mapping[(_0 & 0xC0U) >> 6U | (_1 & 0x0FU) << 2U];
*dst_cur++ = base64_mapping[ _0 & 0x3FU];
}
/* remainder part */
size_t rst = src_beg + cnt - src_cur;
if (rst == 1U) {
uint8_t _2 = *src_cur++;
*dst_cur++ = base64_mapping[ _2 >> 2U];
*dst_cur++ = base64_mapping[(_2 & 0x03U) << 4U];
} else if (rst == 2U) {
uint8_t _2 = *src_cur++;
uint8_t _1 = *src_cur++;
*dst_cur++ = base64_mapping[ _2 >> 2U];
*dst_cur++ = base64_mapping[(_2 & 0x03U) << 4U | (_1 & 0xF0U) >> 4U];
*dst_cur++ = base64_mapping[(_1 & 0x0FU) << 2U];
}
/* padding */
switch (rst)
{
case 1U: *dst_cur++ = base64_padding;
/* fallthrough */
case 2U: *dst_cur++ = base64_padding;
/* fallthrough */
default: *dst_cur = 0;
break;
}
return static_cast<size_t>(dst_cur - dst_beg);
}
int base64::icvCalcStructSize(const char *dt, int initial_size) {
int size = cv::fs::calcElemSize( dt, initial_size );
size_t elem_max_size = 0;
for ( const char * type = dt; *type != '\0'; type++ ) {
switch ( *type )
{
case 'u': { elem_max_size = std::max( elem_max_size, sizeof(uchar ) ); break; }
case 'c': { elem_max_size = std::max( elem_max_size, sizeof(schar ) ); break; }
case 'w': { elem_max_size = std::max( elem_max_size, sizeof(ushort) ); break; }
case 's': { elem_max_size = std::max( elem_max_size, sizeof(short ) ); break; }
case 'i': { elem_max_size = std::max( elem_max_size, sizeof(int ) ); break; }
case 'f': { elem_max_size = std::max( elem_max_size, sizeof(float ) ); break; }
case 'd': { elem_max_size = std::max( elem_max_size, sizeof(double) ); break; }
default: break;
}
}
size = cvAlign( size, static_cast<int>(elem_max_size) );
return size;
}
size_t base64::base64_encode_buffer_size(size_t cnt, bool is_end_with_zero) {
size_t additional = static_cast<size_t>(is_end_with_zero == true);
return (cnt + 2U) / 3U * 4U + additional;
}
base64::Base64Writer::Base64Writer(cv::FileStorage::Impl& fs, bool can_indent)
: emitter(new Base64ContextEmitter(fs, can_indent))
, data_type_string()
{
CV_Assert(fs.write_mode);
}
void base64::Base64Writer::write(const void* _data, size_t len, const char* dt)
{
check_dt(dt);
RawDataToBinaryConvertor convertor(_data, static_cast<int>(len), data_type_string);
emitter->write(convertor);
}
template<typename _to_binary_convertor_t> inline
void base64::Base64Writer::write(_to_binary_convertor_t & convertor, const char* dt)
{
check_dt(dt);
emitter->write(convertor);
}
base64::Base64Writer::~Base64Writer()
{
delete emitter;
}
void base64::Base64Writer::check_dt(const char* dt)
{
if ( dt == 0 )
CV_Error( cv::Error::StsBadArg, "Invalid \'dt\'." );
else if (data_type_string.empty()) {
data_type_string = dt;
/* output header */
std::string buffer = make_base64_header(dt);
const uchar * beg = reinterpret_cast<const uchar *>(buffer.data());
const uchar * end = beg + buffer.size();
emitter->write(beg, end);
} else if ( data_type_string != dt )
CV_Error( cv::Error::StsBadArg, "\'dt\' does not match." );
}
base64::RawDataToBinaryConvertor::RawDataToBinaryConvertor(const void* src, int len, const std::string & dt)
: beg(reinterpret_cast<const uchar *>(src))
, cur(0)
, end(0)
{
CV_Assert(src);
CV_Assert(!dt.empty());
CV_Assert(len > 0);
/* calc step and to_binary_funcs */
step_packed = make_to_binary_funcs(dt);
end = beg;
cur = beg;
step = icvCalcStructSize(dt.c_str(), 0);
end = beg + static_cast<size_t>(len);
}
inline base64::RawDataToBinaryConvertor& base64::RawDataToBinaryConvertor::operator >>(uchar * & dst)
{
CV_DbgAssert(*this);
for (size_t i = 0U, n = to_binary_funcs.size(); i < n; i++) {
elem_to_binary_t & pack = to_binary_funcs[i];
pack.func(cur + pack.offset, dst + pack.offset_packed);
}
cur += step;
dst += step_packed;
return *this;
}
inline base64::RawDataToBinaryConvertor::operator bool() const
{
return cur < end;
}
size_t base64::RawDataToBinaryConvertor::make_to_binary_funcs(const std::string &dt)
{
size_t cnt = 0;
size_t offset = 0;
size_t offset_packed = 0;
char type = '\0';
std::istringstream iss(dt);
while (!iss.eof()) {
if (!(iss >> cnt)) {
iss.clear();
cnt = 1;
}
CV_Assert(cnt > 0U);
if (!(iss >> type))
break;
while (cnt-- > 0)
{
elem_to_binary_t pack;
size_t size = 0;
switch (type)
{
case 'u':
case 'c':
size = sizeof(uchar);
pack.func = to_binary<uchar>;
break;
case 'w':
case 's':
size = sizeof(ushort);
pack.func = to_binary<ushort>;
break;
case 'i':
size = sizeof(uint);
pack.func = to_binary<uint>;
break;
case 'f':
size = sizeof(float);
pack.func = to_binary<float>;
break;
case 'd':
size = sizeof(double);
pack.func = to_binary<double>;
break;
case 'r':
default:
CV_Error(cv::Error::StsError, "type is not supported");
};
offset = static_cast<size_t>(cvAlign(static_cast<int>(offset), static_cast<int>(size)));
pack.offset = offset;
offset += size;
pack.offset_packed = offset_packed;
offset_packed += size;
to_binary_funcs.push_back(pack);
}
}
CV_Assert(iss.eof());
return offset_packed;
}
}