#include "cgiparam.h"
#include <library/cpp/string_utils/scan/scan.h>
#include <library/cpp/string_utils/quote/quote.h>
#include <util/generic/singleton.h>
TCgiParameters::TCgiParameters(std::initializer_list<std::pair<TString, TString>> il) {
for (const auto& item : il) {
insert(item);
}
}
const TString& TCgiParameters::Get(const TStringBuf name, size_t numOfValue) const noexcept {
const auto it = Find(name, numOfValue);
return end() == it ? Default<TString>() : it->second;
}
bool TCgiParameters::Erase(const TStringBuf name, size_t pos) {
const auto pair = equal_range(name);
for (auto it = pair.first; it != pair.second; ++it, --pos) {
if (0 == pos) {
erase(it);
return true;
}
}
return false;
}
bool TCgiParameters::Erase(const TStringBuf name, const TStringBuf val) {
const auto pair = equal_range(name);
bool found = false;
for (auto it = pair.first; it != pair.second;) {
if (val == it->second) {
it = erase(it);
found = true;
} else {
++it;
}
}
return found;
}
size_t TCgiParameters::EraseAll(const TStringBuf name) {
size_t num = 0;
const auto pair = equal_range(name);
for (auto it = pair.first; it != pair.second; erase(it++), ++num)
;
return num;
}
void TCgiParameters::JoinUnescaped(const TStringBuf key, char sep, TStringBuf val) {
const auto pair = equal_range(key);
auto it = pair.first;
if (it == pair.second) { // not found
if (val.IsInited()) {
emplace_hint(it, TString(key), TString(val));
}
} else {
TString& dst = it->second;
for (++it; it != pair.second; erase(it++)) {
dst += sep;
dst.AppendNoAlias(it->second.data(), it->second.size());
}
if (val.IsInited()) {
dst += sep;
dst += val;
}
}
}
static inline TString DoUnescape(const TStringBuf s) {
TString res;
res.reserve(CgiUnescapeBufLen(s.size()));
res.ReserveAndResize(CgiUnescape(res.begin(), s).size());
return res;
}
void TCgiParameters::InsertEscaped(const TStringBuf name, const TStringBuf value) {
InsertUnescaped(DoUnescape(name), DoUnescape(value));
}
template <bool addAll, class F>
static inline void DoScan(const TStringBuf s, F& f) {
ScanKeyValue<addAll, '&', '='>(s, f);
}
struct TAddEscaped {
TCgiParameters* C;
inline void operator()(const TStringBuf key, const TStringBuf val) {
C->InsertEscaped(key, val);
}
};
void TCgiParameters::Scan(const TStringBuf query, bool form) {
Flush();
form ? ScanAdd(query) : ScanAddAll(query);
}
void TCgiParameters::ScanAdd(const TStringBuf query) {
TAddEscaped f = {this};
DoScan<false>(query, f);
}
void TCgiParameters::ScanAddUnescaped(const TStringBuf query) {
auto f = [this](const TStringBuf key, const TStringBuf val) {
this->InsertUnescaped(key, val);
};
DoScan<false>(query, f);
}
void TCgiParameters::ScanAddAllUnescaped(const TStringBuf query) {
auto f = [this](const TStringBuf key, const TStringBuf val) {
this->InsertUnescaped(key, val);
};
DoScan<true>(query, f);
}
void TCgiParameters::ScanAddAll(const TStringBuf query) {
TAddEscaped f = {this};
DoScan<true>(query, f);
}
TString TCgiParameters::Print() const {
TString res;
res.reserve(PrintSize());
const char* end = Print(res.begin());
res.ReserveAndResize(end - res.data());
return res;
}
char* TCgiParameters::Print(char* res) const {
if (empty()) {
return res;
}
for (auto i = begin();;) {
res = CGIEscape(res, i->first);
*res++ = '=';
res = CGIEscape(res, i->second);
if (++i == end()) {
break;
}
*res++ = '&';
}
return res;
}
size_t TCgiParameters::PrintSize() const noexcept {
size_t res = size(); // for '&'
for (const auto& i : *this) {
res += CgiEscapeBufLen(i.first.size() + i.second.size()); // extra zero will be used for '='
}
return res;
}
TString TCgiParameters::QuotedPrint(const char* safe) const {
if (empty()) {
return TString();
}
TString res;
res.ReserveAndResize(PrintSize());
char* ptr = res.begin();
for (auto i = begin();;) {
ptr = Quote(ptr, i->first, safe);
*ptr++ = '=';
ptr = Quote(ptr, i->second, safe);
if (++i == end()) {
break;
}
*ptr++ = '&';
}
res.ReserveAndResize(ptr - res.data());
return res;
}
TCgiParameters::const_iterator TCgiParameters::Find(const TStringBuf name, size_t pos) const noexcept {
const auto pair = equal_range(name);
for (auto it = pair.first; it != pair.second; ++it, --pos) {
if (0 == pos) {
return it;
}
}
return end();
}
bool TCgiParameters::Has(const TStringBuf name, const TStringBuf value) const noexcept {
const auto pair = equal_range(name);
for (auto it = pair.first; it != pair.second; ++it) {
if (value == it->second) {
return true;
}
}
return false;
}
TQuickCgiParam::TQuickCgiParam(const TStringBuf cgiParamStr) {
UnescapeBuf.reserve(CgiUnescapeBufLen(cgiParamStr.size()));
char* buf = UnescapeBuf.begin();
auto f = [this, &buf](const TStringBuf key, const TStringBuf val) {
TStringBuf name = CgiUnescapeBuf(buf, key);
buf += name.size() + 1;
TStringBuf value = CgiUnescapeBuf(buf, val);
buf += value.size() + 1;
Y_ASSERT(buf <= UnescapeBuf.begin() + UnescapeBuf.capacity() + 1 /*trailing zero*/);
emplace(name, value);
};
DoScan<false>(cgiParamStr, f);
if (buf != UnescapeBuf.begin()) {
UnescapeBuf.ReserveAndResize(buf - UnescapeBuf.begin() - 1 /*trailing zero*/);
}
}
const TStringBuf& TQuickCgiParam::Get(const TStringBuf name, size_t pos) const noexcept {
const auto pair = equal_range(name);
for (auto it = pair.first; it != pair.second; ++it, --pos) {
if (0 == pos) {
return it->second;
}
}
return Default<TStringBuf>();
}
bool TQuickCgiParam::Has(const TStringBuf name, const TStringBuf value) const noexcept {
const auto pair = equal_range(name);
for (auto it = pair.first; it != pair.second; ++it) {
if (value == it->second) {
return true;
}
}
return false;
}