-
Notifications
You must be signed in to change notification settings - Fork 313
/
Copy pathencoding.cpp
133 lines (103 loc) · 3.96 KB
/
encoding.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <utils/encoding.hpp>
#include <cerrno>
#include <type_traits>
#include <vector>
#include <iconv.h>
#include <boost/lockfree/stack.hpp>
#include <userver/logging/log.hpp>
#include <userver/utils/strerror.hpp>
USERVER_NAMESPACE_BEGIN
namespace utils::encoding {
namespace {
const auto kIconvEmpty = reinterpret_cast<iconv_t>(-1);
constexpr auto kIconvError = static_cast<size_t>(-1);
struct IconvDeleter {
void operator()(iconv_t iconv) const noexcept { ::iconv_close(iconv); }
};
using IconvHandle = std::unique_ptr<std::remove_pointer_t<iconv_t>, IconvDeleter>;
[[nodiscard]] bool Reset(IconvHandle& handle) {
const auto res = ::iconv(handle.get(), nullptr, nullptr, nullptr, nullptr);
if (res == kIconvError) {
const auto old_errno = errno;
LOG_ERROR() << "error in iconv state reset (" << old_errno << "): " << utils::strerror(old_errno);
return false;
}
return true;
}
} // namespace
class Converter::Impl {
public:
Impl(std::string&& enc_from, std::string&& enc_to) : enc_from_(std::move(enc_from)), enc_to_(std::move(enc_to)) {}
~Impl() {
iconv_t cd = kIconvEmpty;
while (pool_.pop(cd)) {
IconvDeleter{}(cd);
}
}
IconvHandle Pop() const {
iconv_t cd = kIconvEmpty;
if (pool_.pop(cd)) return IconvHandle{cd};
errno = 0;
cd = ::iconv_open(enc_to_.c_str(), enc_from_.c_str());
if (cd == kIconvEmpty) {
const auto old_errno = errno;
LOG_ERROR() << "error in iconv_open (" << old_errno << "): " << utils::strerror(old_errno);
throw std::runtime_error(std::string("iconv_open: ") + utils::strerror(old_errno));
}
return IconvHandle{cd};
}
void Push(IconvHandle&& handle) const {
if (pool_.bounded_push(handle.get())) {
[[maybe_unused]] auto* ptr = handle.release();
}
}
private:
const std::string enc_from_;
const std::string enc_to_;
static const size_t kMaxIconvBufferCount = 8;
using IconvStack = boost::lockfree::stack<iconv_t, boost::lockfree::capacity<kMaxIconvBufferCount>>;
mutable IconvStack pool_;
};
Converter::Converter(std::string enc_from, std::string enc_to) : impl_(std::move(enc_from), std::move(enc_to)) {}
Converter::~Converter() = default;
bool Converter::Convert(const char* data, size_t size, std::vector<char>& out) const {
static constexpr size_t kCoef = 2;
static constexpr size_t kCoefLimit = 16;
auto iconv_handle = impl_->Pop();
for (size_t out_buf_size = size; out_buf_size <= size * kCoefLimit; out_buf_size *= kCoef) {
if (!Reset(iconv_handle)) return false;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
char* pin = const_cast<char*>(data); // it really will not change
size_t in_size = size;
out.resize(out_buf_size);
char* pout = out.data();
size_t out_size = out.size();
errno = 0;
const size_t res = ::iconv(iconv_handle.get(), &pin, &in_size, &pout, &out_size);
if (res == kIconvError) {
const auto old_errno = errno;
if (old_errno != E2BIG) {
LOG_ERROR() << "error in iconv (" << old_errno << "): " << utils::strerror(old_errno);
return false;
}
} else {
out.resize(out.size() - out_size);
impl_->Push(std::move(iconv_handle));
return true;
}
}
LOG_ERROR() << "an output buffer size is too big: " << (size * kCoefLimit);
impl_->Push(std::move(iconv_handle));
return false;
}
bool Converter::Convert(const std::string& in, std::string& out) const {
std::vector<char> out_vec;
if (!Convert(in.data(), in.size(), out_vec)) return false;
out.assign(out_vec.data(), out_vec.size());
return true;
}
bool Converter::Convert(const std::string& in, std::vector<char>& out) const {
return Convert(in.data(), in.size(), out);
}
} // namespace utils::encoding
USERVER_NAMESPACE_END