1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef C2UTILS_PARAM_UTILS_H_
18#define C2UTILS_PARAM_UTILS_H_
19
20#include <C2Param.h>
21#include <util/_C2MacroUtils.h>
22
23#include <iostream>
24
25/** \file
26 * Utilities for parameter handling to be used by Codec2 implementations.
27 */
28
29namespace android {
30
31/// \cond INTERNAL
32
33/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
34
35/**
36 * Utility class that allows ignoring enum value assignment (e.g. both '(_C2EnumConst)kValue = x'
37 * and '(_C2EnumConst)kValue' will eval to kValue.
38 */
39template<typename T>
40class _C2EnumConst {
41public:
42    // implicit conversion from T
43    inline _C2EnumConst(T value) : _mValue(value) {}
44    // implicit conversion to T
45    inline operator T() { return _mValue; }
46    // implicit conversion to C2Value::Primitive
47    inline operator C2Value::Primitive() { return (T)_mValue; }
48    // ignore assignment and return T here to avoid implicit conversion to T later
49    inline T &operator =(T value __unused) { return _mValue; }
50private:
51    T _mValue;
52};
53
54/// mapper to get name of enum
55/// \note this will contain any initialization, which we will remove when converting to lower-case
56#define _C2_GET_ENUM_NAME(x, y) #x
57/// mapper to get value of enum
58#define _C2_GET_ENUM_VALUE(x, type) (_C2EnumConst<type>)x
59
60/// \endcond
61
62#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
63template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
64    return C2ParamUtils::sanitizeEnumValues( \
65            std::vector<C2Value::Primitive> { _C2_MAP(_C2_GET_ENUM_VALUE, type, __VA_ARGS__) }, \
66            { _C2_MAP(_C2_GET_ENUM_NAME, type, __VA_ARGS__) }, \
67            prefix); \
68}
69
70#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...) \
71template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
72    return C2ParamUtils::customEnumValues( \
73            std::vector<std::pair<C2StringLiteral, name>> names); \
74}
75
76
77class C2ParamUtils {
78private:
79    static size_t countLeadingUnderscores(C2StringLiteral a) {
80        size_t i = 0;
81        while (a[i] == '_') {
82            ++i;
83        }
84        return i;
85    }
86
87    static size_t countMatching(C2StringLiteral a, const C2String &b) {
88        for (size_t i = 0; i < b.size(); ++i) {
89            if (!a[i] || a[i] != b[i]) {
90                return i;
91            }
92        }
93        return b.size();
94    }
95
96    // ABCDef => abc-def
97    // ABCD2ef => abcd2-ef // 0
98    // ABCD2Ef => ancd2-ef // -1
99    // AbcDef => abc-def // -1
100    // Abc2Def => abc-2def
101    // Abc2def => abc-2-def
102    // _Yo => _yo
103    // _yo => _yo
104    // C2_yo => c2-yo
105    // C2__yo => c2-yo
106
107    static C2String camelCaseToDashed(C2String name) {
108        enum {
109            kNone = '.',
110            kLower = 'a',
111            kUpper = 'A',
112            kDigit = '1',
113            kDash = '-',
114            kUnderscore = '_',
115        } type = kNone;
116        size_t word_start = 0;
117        for (size_t ix = 0; ix < name.size(); ++ix) {
118            /* std::cout << name.substr(0, word_start) << "|"
119                    << name.substr(word_start, ix - word_start) << "["
120                    << name.substr(ix, 1) << "]" << name.substr(ix + 1)
121                    << ": " << (char)type << std::endl; */
122            if (isupper(name[ix])) {
123                if (type == kLower) {
124                    name.insert(ix++, 1, '-');
125                    word_start = ix;
126                }
127                name[ix] = tolower(name[ix]);
128                type = kUpper;
129            } else if (islower(name[ix])) {
130                if (type == kDigit && ix > 0) {
131                    name.insert(ix++, 1, '-');
132                    word_start = ix;
133                } else if (type == kUpper && ix > word_start + 1) {
134                    name.insert(ix++ - 1, 1, '-');
135                    word_start = ix - 1;
136                }
137                type = kLower;
138            } else if (isdigit(name[ix])) {
139                if (type == kLower) {
140                    name.insert(ix++, 1, '-');
141                    word_start = ix;
142                }
143                type = kDigit;
144            } else if (name[ix] == '_') {
145                if (type == kDash) {
146                    name.erase(ix--, 1);
147                } else if (type != kNone && type != kUnderscore) {
148                    name[ix] = '-';
149                    type = kDash;
150                    word_start = ix + 1;
151                } else {
152                    type = kUnderscore;
153                    word_start = ix + 1;
154                }
155            } else {
156                name.resize(ix);
157            }
158        }
159        // std::cout << "=> " << name << std::endl;
160        return name;
161    }
162
163    static std::vector<C2String> sanitizeEnumValueNames(
164            const std::vector<C2StringLiteral> names,
165            C2StringLiteral _prefix = NULL) {
166        std::vector<C2String> sanitizedNames;
167        C2String prefix;
168        size_t extraUnderscores = 0;
169        bool first = true;
170        if (_prefix) {
171            extraUnderscores = countLeadingUnderscores(_prefix);
172            prefix = _prefix + extraUnderscores;
173            first = false;
174            // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
175        }
176
177        // calculate prefix and minimum leading underscores
178        for (C2StringLiteral s : names) {
179            // std::cout << s << std::endl;
180            size_t underscores = countLeadingUnderscores(s);
181            if (first) {
182                extraUnderscores = underscores;
183                prefix = s + underscores;
184                first = false;
185            } else {
186                size_t matching = countMatching(
187                    s + underscores,
188                    prefix);
189                prefix.resize(matching);
190                extraUnderscores = std::min(underscores, extraUnderscores);
191            }
192            // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
193            if (prefix.size() == 0 && extraUnderscores == 0) {
194                break;
195            }
196        }
197
198        // we swallow the first underscore after upper case prefixes
199        bool upperCasePrefix = true;
200        for (size_t i = 0; i < prefix.size(); ++i) {
201            if (islower(prefix[i])) {
202                upperCasePrefix = false;
203                break;
204            }
205        }
206
207        for (C2StringLiteral s : names) {
208            size_t underscores = countLeadingUnderscores(s);
209            C2String sanitized = C2String(s, underscores - extraUnderscores);
210            sanitized.append(s + prefix.size() + underscores +
211                        (upperCasePrefix && s[prefix.size() + underscores] == '_'));
212            sanitizedNames.push_back(camelCaseToDashed(sanitized));
213        }
214
215        for (C2String s : sanitizedNames) {
216            std::cout << s << std::endl;
217        }
218
219        return sanitizedNames;
220    }
221
222    friend class C2ParamTest_ParamUtilsTest_Test;
223
224public:
225    static std::vector<C2String> getEnumValuesFromString(C2StringLiteral value) {
226        std::vector<C2String> foundNames;
227        size_t pos = 0, len = strlen(value);
228        do {
229            size_t endPos = strcspn(value + pos, " ,=") + pos;
230            if (endPos > pos) {
231                foundNames.emplace_back(value + pos, endPos - pos);
232            }
233            if (value[endPos] && value[endPos] != ',') {
234                endPos += strcspn(value + endPos, ",");
235            }
236            pos = strspn(value + endPos, " ,") + endPos;
237        } while (pos < len);
238        return foundNames;
239    }
240
241    template<typename T>
242    static C2FieldDescriptor::named_values_type sanitizeEnumValues(
243            std::vector<T> values,
244            std::vector<C2StringLiteral> names,
245            C2StringLiteral prefix = NULL) {
246        C2FieldDescriptor::named_values_type namedValues;
247        std::vector<C2String> sanitizedNames = sanitizeEnumValueNames(names, prefix);
248        for (size_t i = 0; i < values.size() && i < sanitizedNames.size(); ++i) {
249            namedValues.emplace_back(sanitizedNames[i], values[i]);
250        }
251        return namedValues;
252    }
253
254    template<typename E>
255    static C2FieldDescriptor::named_values_type customEnumValues(
256            std::vector<std::pair<C2StringLiteral, E>> items) {
257        C2FieldDescriptor::named_values_type namedValues;
258        for (auto &item : items) {
259            namedValues.emplace_back(item.first, item.second);
260        }
261        return namedValues;
262    }
263};
264
265/* ---------------------------- UTILITIES FOR PARAMETER REFLECTION ---------------------------- */
266
267/* ======================== UTILITY TEMPLATES FOR PARAMETER REFLECTION ======================== */
268
269#if 1
270template<typename... Params>
271class C2_HIDE _C2Tuple { };
272
273C2_HIDE
274void addC2Params(std::list<const C2FieldDescriptor> &, _C2Tuple<> *) {
275}
276
277template<typename T, typename... Params>
278C2_HIDE
279void addC2Params(std::list<const C2FieldDescriptor> &fields, _C2Tuple<T, Params...> *)
280{
281    //C2Param::index_t index = T::baseIndex;
282    //(void)index;
283    fields.insert(fields.end(), T::fieldList);
284    addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
285}
286
287template<typename... Params>
288C2_HIDE
289std::list<const C2FieldDescriptor> describeC2Params() {
290    std::list<const C2FieldDescriptor> fields;
291    addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
292    return fields;
293}
294
295#endif
296
297/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
298
299}  // namespace android
300
301#endif  // C2UTILS_PARAM_UTILS_H_
302
303