/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define __C2_GENERATE_GLOBAL_VARS__ // to be able to implement the methods defined
#include <C2Enum.h>
#include <util/C2Debug-log.h>
#include <util/C2ParamUtils.h>
#include <utility>
#include <vector>
/** \file
* Utilities for parameter handling to be used by Codec2 implementations.
*/
/// \cond INTERNAL
/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
static size_t countLeadingUnderscores(C2StringLiteral a) {
size_t i = 0;
while (a[i] == '_') {
++i;
}
return i;
}
static size_t countMatching(C2StringLiteral a, const C2String &b) {
for (size_t i = 0; i < b.size(); ++i) {
if (!a[i] || a[i] != b[i]) {
return i;
}
}
return b.size();
}
// ABCDef => abc-def
// ABCD2ef => abcd2-ef // 0
// ABCD2Ef => ancd2-ef // -1
// AbcDef => abc-def // -1
// Abc2Def => abc-2def
// Abc2def => abc-2-def
// _Yo => _yo
// _yo => _yo
// C2_yo => c2-yo
// C2__yo => c2-yo
//static
C2String _C2EnumUtils::camelCaseToDashed(C2String name) {
enum {
kNone = '.',
kLower = 'a',
kUpper = 'A',
kDigit = '1',
kDash = '-',
kUnderscore = '_',
} type = kNone;
size_t word_start = 0;
for (size_t ix = 0; ix < name.size(); ++ix) {
C2_LOG(VERBOSE) << name.substr(0, word_start) << "|"
<< name.substr(word_start, ix - word_start) << "["
<< name.substr(ix, 1) << "]" << name.substr(ix + 1)
<< ": " << (char)type;
if (isupper(name[ix])) {
if (type == kLower) {
name.insert(ix++, 1, '-');
word_start = ix;
}
name[ix] = tolower(name[ix]);
type = kUpper;
} else if (islower(name[ix])) {
if (type == kDigit && ix > 0) {
name.insert(ix++, 1, '-');
word_start = ix;
} else if (type == kUpper && ix > word_start + 1) {
name.insert(ix++ - 1, 1, '-');
word_start = ix - 1;
}
type = kLower;
} else if (isdigit(name[ix])) {
if (type == kLower) {
name.insert(ix++, 1, '-');
word_start = ix;
}
type = kDigit;
} else if (name[ix] == '_') {
if (type == kDash) {
name.erase(ix--, 1);
} else if (type != kNone && type != kUnderscore) {
name[ix] = '-';
type = kDash;
word_start = ix + 1;
} else {
type = kUnderscore;
word_start = ix + 1;
}
} else {
name.resize(ix);
}
}
C2_LOG(VERBOSE) << "=> " << name;
return name;
}
//static
std::vector<C2String> _C2EnumUtils::sanitizeEnumValueNames(
const std::vector<C2StringLiteral> names,
C2StringLiteral _prefix) {
std::vector<C2String> sanitizedNames;
C2String prefix;
size_t extraUnderscores = 0;
bool first = true;
if (_prefix) {
extraUnderscores = countLeadingUnderscores(_prefix);
prefix = _prefix + extraUnderscores;
first = false;
C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
}
// calculate prefix and minimum leading underscores
for (C2StringLiteral s : names) {
C2_LOG(VERBOSE) << s;
size_t underscores = countLeadingUnderscores(s);
if (first) {
extraUnderscores = underscores;
prefix = s + underscores;
first = false;
} else {
size_t matching = countMatching(
s + underscores,
prefix);
prefix.resize(matching);
extraUnderscores = std::min(underscores, extraUnderscores);
}
C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
if (prefix.size() == 0 && extraUnderscores == 0) {
break;
}
}
// we swallow the first underscore after upper case prefixes
bool upperCasePrefix = true;
for (size_t i = 0; i < prefix.size(); ++i) {
if (islower(prefix[i])) {
upperCasePrefix = false;
break;
}
}
for (C2StringLiteral s : names) {
size_t underscores = countLeadingUnderscores(s);
C2String sanitized = C2String(s, underscores - extraUnderscores);
sanitized.append(s + prefix.size() + underscores +
(upperCasePrefix && s[prefix.size() + underscores] == '_'));
sanitizedNames.push_back(camelCaseToDashed(sanitized));
}
for (C2String s : sanitizedNames) {
C2_LOG(VERBOSE) << s;
}
return sanitizedNames;
}
//static
std::vector<C2String> _C2EnumUtils::parseEnumValuesFromString(C2StringLiteral value) {
std::vector<C2String> foundNames;
size_t pos = 0, len = strlen(value);
do {
size_t endPos = strcspn(value + pos, " ,=") + pos;
if (endPos > pos) {
foundNames.emplace_back(value + pos, endPos - pos);
}
if (value[endPos] && value[endPos] != ',') {
endPos += strcspn(value + endPos, ",");
}
pos = strspn(value + endPos, " ,") + endPos;
} while (pos < len);
return foundNames;
}
/// safe(r) parsing from parameter blob
//static
C2Param *C2ParamUtils::ParseFirst(const uint8_t *blob, size_t size) {
// _mSize must fit into size, but really C2Param must also to be a valid param
if (size < sizeof(C2Param)) {
return nullptr;
}
// _mSize must match length
C2Param *param = (C2Param*)blob;
if (param->size() > size) {
return nullptr;
}
return param;
}