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#define __C2_GENERATE_GLOBAL_VARS__ // to be able to implement the methods defined
18#include <C2Enum.h>
19#include <util/C2Debug-log.h>
20#include <util/C2ParamUtils.h>
21
22#include <utility>
23#include <vector>
24
25/** \file
26 * Utilities for parameter handling to be used by Codec2 implementations.
27 */
28
29/// \cond INTERNAL
30
31/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
32
33static size_t countLeadingUnderscores(C2StringLiteral a) {
34    size_t i = 0;
35    while (a[i] == '_') {
36        ++i;
37    }
38    return i;
39}
40
41static size_t countMatching(C2StringLiteral a, const C2String &b) {
42    for (size_t i = 0; i < b.size(); ++i) {
43        if (!a[i] || a[i] != b[i]) {
44            return i;
45        }
46    }
47    return b.size();
48}
49
50// ABCDef => abc-def
51// ABCD2ef => abcd2-ef // 0
52// ABCD2Ef => ancd2-ef // -1
53// AbcDef => abc-def // -1
54// Abc2Def => abc-2def
55// Abc2def => abc-2-def
56// _Yo => _yo
57// _yo => _yo
58// C2_yo => c2-yo
59// C2__yo => c2-yo
60
61//static
62C2String _C2EnumUtils::camelCaseToDashed(C2String name) {
63    enum {
64        kNone = '.',
65        kLower = 'a',
66        kUpper = 'A',
67        kDigit = '1',
68        kDash = '-',
69        kUnderscore = '_',
70    } type = kNone;
71    size_t word_start = 0;
72    for (size_t ix = 0; ix < name.size(); ++ix) {
73        C2_LOG(VERBOSE) << name.substr(0, word_start) << "|"
74                << name.substr(word_start, ix - word_start) << "["
75                << name.substr(ix, 1) << "]" << name.substr(ix + 1)
76                << ": " << (char)type;
77        if (isupper(name[ix])) {
78            if (type == kLower) {
79                name.insert(ix++, 1, '-');
80                word_start = ix;
81            }
82            name[ix] = tolower(name[ix]);
83            type = kUpper;
84        } else if (islower(name[ix])) {
85            if (type == kDigit && ix > 0) {
86                name.insert(ix++, 1, '-');
87                word_start = ix;
88            } else if (type == kUpper && ix > word_start + 1) {
89                name.insert(ix++ - 1, 1, '-');
90                word_start = ix - 1;
91            }
92            type = kLower;
93        } else if (isdigit(name[ix])) {
94            if (type == kLower) {
95                name.insert(ix++, 1, '-');
96                word_start = ix;
97            }
98            type = kDigit;
99        } else if (name[ix] == '_') {
100            if (type == kDash) {
101                name.erase(ix--, 1);
102            } else if (type != kNone && type != kUnderscore) {
103                name[ix] = '-';
104                type = kDash;
105                word_start = ix + 1;
106            } else {
107                type = kUnderscore;
108                word_start = ix + 1;
109            }
110        } else {
111            name.resize(ix);
112        }
113    }
114    C2_LOG(VERBOSE) << "=> " << name;
115    return name;
116}
117
118//static
119std::vector<C2String> _C2EnumUtils::sanitizeEnumValueNames(
120        const std::vector<C2StringLiteral> names,
121        C2StringLiteral _prefix) {
122    std::vector<C2String> sanitizedNames;
123    C2String prefix;
124    size_t extraUnderscores = 0;
125    bool first = true;
126    if (_prefix) {
127        extraUnderscores = countLeadingUnderscores(_prefix);
128        prefix = _prefix + extraUnderscores;
129        first = false;
130        C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
131    }
132
133    // calculate prefix and minimum leading underscores
134    for (C2StringLiteral s : names) {
135        C2_LOG(VERBOSE) << s;
136        size_t underscores = countLeadingUnderscores(s);
137        if (first) {
138            extraUnderscores = underscores;
139            prefix = s + underscores;
140            first = false;
141        } else {
142            size_t matching = countMatching(
143                s + underscores,
144                prefix);
145            prefix.resize(matching);
146            extraUnderscores = std::min(underscores, extraUnderscores);
147        }
148        C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
149        if (prefix.size() == 0 && extraUnderscores == 0) {
150            break;
151        }
152    }
153
154    // we swallow the first underscore after upper case prefixes
155    bool upperCasePrefix = true;
156    for (size_t i = 0; i < prefix.size(); ++i) {
157        if (islower(prefix[i])) {
158            upperCasePrefix = false;
159            break;
160        }
161    }
162
163    for (C2StringLiteral s : names) {
164        size_t underscores = countLeadingUnderscores(s);
165        C2String sanitized = C2String(s, underscores - extraUnderscores);
166        sanitized.append(s + prefix.size() + underscores +
167                    (upperCasePrefix && s[prefix.size() + underscores] == '_'));
168        sanitizedNames.push_back(camelCaseToDashed(sanitized));
169    }
170
171    for (C2String s : sanitizedNames) {
172        C2_LOG(VERBOSE) << s;
173    }
174
175    return sanitizedNames;
176}
177
178//static
179std::vector<C2String> _C2EnumUtils::parseEnumValuesFromString(C2StringLiteral value) {
180    std::vector<C2String> foundNames;
181    size_t pos = 0, len = strlen(value);
182    do {
183        size_t endPos = strcspn(value + pos, " ,=") + pos;
184        if (endPos > pos) {
185            foundNames.emplace_back(value + pos, endPos - pos);
186        }
187        if (value[endPos] && value[endPos] != ',') {
188            endPos += strcspn(value + endPos, ",");
189        }
190        pos = strspn(value + endPos, " ,") + endPos;
191    } while (pos < len);
192    return foundNames;
193}
194
195/// safe(r) parsing from parameter blob
196//static
197C2Param *C2ParamUtils::ParseFirst(const uint8_t *blob, size_t size) {
198    // _mSize must fit into size, but really C2Param must also to be a valid param
199    if (size < sizeof(C2Param)) {
200        return nullptr;
201    }
202    // _mSize must match length
203    C2Param *param = (C2Param*)blob;
204    if (param->size() > size) {
205        return nullptr;
206    }
207    return param;
208}
209
210