aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/cgiparam/cgiparam.h
blob: f4ee174213d64ca9b58b56b64d5aef9d950e0fda (plain) (blame)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#pragma once

#include <library/cpp/iterator/iterate_values.h>

#include <util/generic/iterator_range.h>
#include <util/generic/map.h>
#include <util/generic/strbuf.h>
#include <util/generic/string.h>
 
#include <initializer_list>
 
struct TStringLess { 
    template <class T1, class T2> 
    inline bool operator()(const T1& t1, const T2& t2) const noexcept {
        return TStringBuf(t1) < TStringBuf(t2); 
    } 
}; 
 
class TCgiParameters: public TMultiMap<TString, TString> {
public:
    TCgiParameters() = default;

    explicit TCgiParameters(const TStringBuf cgiParamStr) {
        Scan(cgiParamStr);
    }

    TCgiParameters(std::initializer_list<std::pair<TString, TString>> il);

    void Flush() {
        erase(begin(), end());
    }

    size_t EraseAll(const TStringBuf name);

    size_t NumOfValues(const TStringBuf name) const noexcept {
        return count(name);
    }

    TString operator()() const {
        return Print();
    }

    void Scan(const TStringBuf cgiParStr, bool form = true);
    void ScanAdd(const TStringBuf cgiParStr);
    void ScanAddUnescaped(const TStringBuf cgiParStr);
    void ScanAddAllUnescaped(const TStringBuf cgiParStr);
    void ScanAddAll(const TStringBuf cgiParStr);
 
    /// Returns the string representation of all the stored parameters
    /**
     * @note The returned string has format <name1>=<value1>&<name2>=<value2>&...
     * @note Names and values in the returned string are CGI-escaped.
     */
    TString Print() const;
    char* Print(char* res) const;

    Y_PURE_FUNCTION
    size_t PrintSize() const noexcept;

    /** The same as Print* except that RFC-3986 reserved characters are escaped.
     * @param safe - set of characters to be skipped in escaping
     */
    TString QuotedPrint(const char* safe = "/") const;

    Y_PURE_FUNCTION
    auto Range(const TStringBuf name) const noexcept {
        return IterateValues(MakeIteratorRange(equal_range(name)));
    }

    Y_PURE_FUNCTION
    const_iterator Find(const TStringBuf name, size_t numOfValue = 0) const noexcept;

    Y_PURE_FUNCTION
    bool Has(const TStringBuf name, const TStringBuf value) const noexcept;

    Y_PURE_FUNCTION
    bool Has(const TStringBuf name) const noexcept {
        const auto pair = equal_range(name);
        return pair.first != pair.second;
    }
    /// Returns value by name
    /**
     * @note The returned value is CGI-unescaped.
     */
    Y_PURE_FUNCTION
    const TString& Get(const TStringBuf name, size_t numOfValue = 0) const noexcept;
 
    void InsertEscaped(const TStringBuf name, const TStringBuf value);
 
#if !defined(__GLIBCXX__)
    template <typename TName, typename TValue>
    inline void InsertUnescaped(TName&& name, TValue&& value) {
        // TStringBuf use as TName or TValue is C++17 actually.
        // There is no pair constructor available in C++14 when required type
        // is not implicitly constructible from given type.
        // But libc++ pair allows this with C++14.
        emplace(std::forward<TName>(name), std::forward<TValue>(value));
    }
#else
    template <typename TName, typename TValue>
    inline void InsertUnescaped(TName&& name, TValue&& value) {
        emplace(TString(name), TString(value));
    }
#endif
 
    // replace all values for a given key with new values
    template <typename TIter>
    void ReplaceUnescaped(const TStringBuf key, TIter valuesBegin, const TIter valuesEnd);

    void ReplaceUnescaped(const TStringBuf key, std::initializer_list<TStringBuf> values) {
        ReplaceUnescaped(key, values.begin(), values.end());
    }

    void ReplaceUnescaped(const TStringBuf key, const TStringBuf value) {
        ReplaceUnescaped(key, {value}); 
    }

    // join multiple values into a single one using a separator
    // if val is a [possibly empty] non-NULL string, append it as well
    void JoinUnescaped(const TStringBuf key, char sep, TStringBuf val = TStringBuf());

    bool Erase(const TStringBuf name, size_t numOfValue = 0);
    bool Erase(const TStringBuf name, const TStringBuf val);
 
    inline const char* FormField(const TStringBuf name, size_t numOfValue = 0) const {
        const_iterator it = Find(name, numOfValue);
 
        if (it == end()) {
            return nullptr;
        } 

        return it->second.data();
    }
};

template <typename TIter>
void TCgiParameters::ReplaceUnescaped(const TStringBuf key, TIter valuesBegin, const TIter valuesEnd) {
    const auto oldRange = equal_range(key);
    auto current = oldRange.first;

    // reuse as many existing nodes as possible (probably none)
    for (; valuesBegin != valuesEnd && current != oldRange.second; ++valuesBegin, ++current) { 
        current->second = *valuesBegin;
    }

    // if there were more nodes than we need to insert then erase remaining ones
    for (; current != oldRange.second; erase(current++)) { 
    }

    // if there were less nodes than we need to insert then emplace the rest of the range
    if (valuesBegin != valuesEnd) {
        const TString keyStr = TString(key);
        for (; valuesBegin != valuesEnd; ++valuesBegin) { 
            emplace_hint(oldRange.second, keyStr, TString(*valuesBegin));
        }
    }
}

/** TQuickCgiParam is a faster non-editable version of TCgiParameters.
 * Care should be taken when replacing:
 *  - note that the result of Get() is invalidated when TQuickCgiParam object is destroyed.
 */

class TQuickCgiParam: public TMultiMap<TStringBuf, TStringBuf> {
public:
    TQuickCgiParam() = default;

    explicit TQuickCgiParam(const TStringBuf cgiParamStr);

    Y_PURE_FUNCTION
    bool Has(const TStringBuf name, const TStringBuf value) const noexcept;

    Y_PURE_FUNCTION
    bool Has(const TStringBuf name) const noexcept {
        const auto pair = equal_range(name);
        return pair.first != pair.second;
    }

    Y_PURE_FUNCTION
    const TStringBuf& Get(const TStringBuf name, size_t numOfValue = 0) const noexcept;

private:
    TString UnescapeBuf;
};