Home | History | Annotate | Download | only in util
      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 
     33 static size_t countLeadingUnderscores(C2StringLiteral a) {
     34     size_t i = 0;
     35     while (a[i] == '_') {
     36         ++i;
     37     }
     38     return i;
     39 }
     40 
     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
     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
    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
    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
    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