mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
5fb3869775
* 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>
371 lines
11 KiB
C++
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;
|
|
}
|
|
|
|
}
|