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 
countLeadingUnderscores(C2StringLiteral a)33 static size_t countLeadingUnderscores(C2StringLiteral a) {
34     size_t i = 0;
35     while (a[i] == '_') {
36         ++i;
37     }
38     return i;
39 }
40 
countMatching(C2StringLiteral a,const C2String & b)41 static 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
camelCaseToDashed(C2String name)62 C2String _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
sanitizeEnumValueNames(const std::vector<C2StringLiteral> names,C2StringLiteral _prefix)119 std::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
parseEnumValuesFromString(C2StringLiteral value)179 std::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
ParseFirst(const uint8_t * blob,size_t size)197 C2Param *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