LCOV - code coverage report
Current view: top level - core/data - SPDataDecodeSerenity.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 196 311 63.0 %
Date: 2024-05-12 00:16:13 Functions: 9 20 45.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #ifndef STAPPLER_DATA_SPDATADECODESERENITY_H_
      25             : #define STAPPLER_DATA_SPDATADECODESERENITY_H_
      26             : 
      27             : #include "SPDataDecodeJson.h"
      28             : #include "SPDataValue.h"
      29             : 
      30             : namespace STAPPLER_VERSIONIZED stappler::data::serenity {
      31             : 
      32             : template <typename Interface>
      33             : struct Decoder : public Interface::AllocBaseType {
      34             :         using InterfaceType = Interface;
      35             :         using ValueType = ValueTemplate<Interface>;
      36             :         using StringType = typename InterfaceType::StringType;
      37             :         using BytesType = typename InterfaceType::BytesType;
      38             : 
      39             :         enum BackType {
      40             :                 BackIsPlain,
      41             :                 BackIsPlainList,
      42             :                 BackIsPlainStop,
      43             :                 BackIsArray,
      44             :                 BackIsDict,
      45             :                 BackIsGeneric,
      46             :         };
      47             : 
      48         775 :         Decoder(StringView &r) : backType(BackIsGeneric), r(r), back(nullptr) {
      49         775 :                 stack.reserve(10);
      50         775 :         }
      51             : 
      52             :         inline void parseBufferString(StringType &ref);
      53             :         inline void parseNumber(StringView &token, ValueType &ref) SPINLINE;
      54             : 
      55             :         inline void parsePlainToken(ValueType &current, StringView v);
      56             : 
      57             :         inline void transformToDict(ValueType &current);
      58             : 
      59             :         void parse(ValueType &val);
      60             : 
      61        2300 :         inline void push(BackType t, ValueType *v) {
      62        2300 :                 if (t != BackIsPlain && t != BackIsGeneric) {
      63           0 :                         ++ r;
      64             :                 }
      65        2300 :                 back = v;
      66        2300 :                 stack.push_back(pair(t, v));
      67        2300 :                 backType = t;
      68        2300 :         }
      69             : 
      70        3025 :         inline void pop() {
      71        3025 :                 if (backType != BackIsPlain && backType != BackIsPlainList && backType != BackIsPlainStop) {
      72        1250 :                         r ++;
      73             :                 }
      74        3025 :                 stack.pop_back();
      75        3025 :                 if (stack.empty()) {
      76         725 :                         back = nullptr;
      77         725 :                         backType = BackIsGeneric;
      78             :                 } else {
      79        2300 :                         back = stack.back().second;
      80        2300 :                         backType = stack.back().first;
      81             :                 }
      82        3025 :         }
      83             : 
      84             :         bool stop = false;
      85             :         BackType backType;
      86             :         StringView r;
      87             :         ValueType *back;
      88             :         typename InterfaceType::template ArrayType<Pair<BackType, ValueType *>> stack;
      89             : };
      90             : 
      91             : template <typename Interface>
      92             : inline void Decoder<Interface>::parseNumber(StringView &token, ValueType &result) {
      93         650 :         bool isFloat = false;
      94         650 :         if (token == "+inf") {
      95           0 :                 result._type = ValueType::Type::DOUBLE;
      96           0 :                 result.doubleVal = NumericLimits<double>::infinity();
      97         650 :         } else if (token == "-inf") {
      98           0 :                 result._type = ValueType::Type::DOUBLE;
      99           0 :                 result.doubleVal = - NumericLimits<double>::infinity();
     100             :         } else {
     101         650 :                 auto data = token.data();
     102         650 :                 auto size = token.size();
     103         650 :                 auto value = json::decodeNumber(token, isFloat);
     104         650 :                 if (value.empty()) {
     105           0 :                         return;
     106         650 :                 } else if (value.size() != size) {
     107         250 :                         result._type = ValueType::Type::CHARSTRING;
     108         250 :                         result.strVal = new StringType(data, size);
     109             :                 } else {
     110         400 :                         if (isFloat) {
     111           0 :                                 value.readDouble().unwrap([&] (double v) {
     112           0 :                                         result._type = ValueType::Type::DOUBLE;
     113           0 :                                         result.doubleVal = v;
     114             :                                 });
     115             :                         } else {
     116         800 :                                 value.readInteger().unwrap([&] (int64_t v) {
     117         400 :                                         result._type = ValueType::Type::INTEGER;
     118         400 :                                         result.intVal = v;
     119             :                                 });
     120             :                         }
     121             :                 }
     122             :         }
     123             : }
     124             : 
     125             : template <typename Interface>
     126        3525 : inline void Decoder<Interface>::parsePlainToken(ValueType &current, StringView token) {
     127        3525 :         switch (token[0]) {
     128         650 :         case '0': case '1': case '2': case '3': case '4': case '5': case '6':
     129             :         case '7': case '8': case '9': case '+': case '-':
     130             :                 parseNumber(token, current);
     131         650 :                 return;
     132             :                 break;
     133         450 :         case 't':
     134         450 :                 if (token == "true") {
     135           0 :                         current._type = ValueType::Type::BOOLEAN;
     136           0 :                         current.boolVal = true;
     137           0 :                         return;
     138             :                 }
     139         450 :                 break;
     140           0 :         case 'f':
     141           0 :                 if (token == "false") {
     142           0 :                         current._type = ValueType::Type::BOOLEAN;
     143           0 :                         current.boolVal = false;
     144           0 :                         return;
     145             :                 }
     146           0 :                 break;
     147           0 :         case 'n':
     148           0 :                 if (token == "nan") {
     149           0 :                         current._type = ValueType::Type::DOUBLE;
     150           0 :                         current.doubleVal = nan();
     151           0 :                         return;
     152           0 :                 } else if (token == "null") {
     153           0 :                         current._type = ValueType::Type::EMPTY;
     154           0 :                         return;
     155             :                 }
     156           0 :                 break;
     157         425 :         case 'i':
     158         425 :                 if (token == "inf") {
     159           0 :                         current._type = ValueType::Type::DOUBLE;
     160           0 :                         current.doubleVal = NumericLimits<double>::infinity();
     161           0 :                         return;
     162             :                 }
     163         425 :                 break;
     164           0 :         case '~':
     165           0 :                 current._type = ValueType::Type::BYTESTRING;
     166           0 :                 current.bytesVal = new BytesType();
     167           0 :                 string::urldecode(*current.bytesVal, token);
     168           0 :                 return;
     169             :                 break;
     170             :         }
     171        2875 :         current._type = ValueType::Type::CHARSTRING;
     172        5750 :         current.strVal = new StringType();
     173        2875 :         string::urldecode(*current.strVal, token);
     174             : }
     175             : 
     176             : template <typename Interface>
     177          25 : inline void Decoder<Interface>::transformToDict(ValueType &current) {
     178          25 :         typename ValueType::DictionaryType dict;
     179          75 :         for (auto &it : *current.arrayVal) {
     180          50 :                 auto str = it.asString();
     181          50 :                 if (!str.empty()) {
     182          50 :                         dict.emplace(move(str), ValueType(true));
     183             :                 } else {
     184           0 :                         log::error("DataSerenityDecoder", "Invalid token within SubArray");
     185             :                 }
     186             :         }
     187          25 :         current = ValueType(move(dict));
     188          25 : }
     189             : 
     190             : using TokenSpecials = StringView::Chars<'/', '?', '@', '-', '.', '_', '!', '$', '\'', '*', '+', '%'>;
     191             : 
     192             : template <typename Interface>
     193         775 : void Decoder<Interface>::parse(ValueType &val) {
     194         775 :         backType = BackIsGeneric;
     195         775 :         back = &val;
     196         775 :         stack.push_back(pair(backType, back));
     197             : 
     198         775 :         StringType key;
     199             :         do {
     200        7500 :                 r.skipUntil<TokenSpecials, StringView::Chars<'(', '~', ';', ')', ','>, StringView::CharGroup<CharGroupId::Alphanumeric>, chars::UniChar>();
     201        7500 :                 switch (backType) {
     202        1775 :                 case BackIsPlain:
     203        1775 :                         if (r.is(')') || r.is(';')) {
     204           0 :                                 pop();
     205        1775 :                         } else if (r.is(',')) {
     206           0 :                                 typename ValueType::ArrayType arr;
     207           0 :                                 arr.emplace_back(move(back));
     208           0 :                                 *back = ValueType(move(arr));
     209           0 :                                 backType = stack.back().first = BackIsPlainList;
     210        1775 :                         } else if (r.is('(') || r.is("~(")) {
     211           0 :                                 backType = stack.back().first = BackIsPlainStop;
     212           0 :                                 push(BackIsGeneric, back);
     213             :                         } else {
     214        1775 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     215        1775 :                                 StringView token = r.readUntil<StringView::Chars<'~', ',', ';', '(', ')'>, StringView::CharGroup<CharGroupId::WhiteSpace>>();
     216        1775 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     217        1775 :                                 if (r.is(':')) {
     218           0 :                                         log::error("DataSerenityDecoder", "Colon sequence within plain list is invalid");
     219           0 :                                         stop = true;
     220           0 :                                         break;
     221        1775 :                                 } else if (token.empty()) {
     222             :                                         // do nothing
     223        1775 :                                 } else if (r.is('(') || r.is("~(")) {
     224           0 :                                         key.clear(); string::urldecode(key, token);
     225           0 :                                         back->_type = ValueType::Type::DICTIONARY;
     226           0 :                                         back->dictVal = new typename ValueType::DictionaryType();
     227           0 :                                         backType = stack.back().first = BackIsPlainList;
     228           0 :                                         push(BackIsGeneric, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     229        1775 :                                 } else if (r.is(',')) {
     230         475 :                                         back->_type = ValueType::Type::ARRAY;
     231         950 :                                         back->arrayVal = new typename ValueType::ArrayType();
     232         475 :                                         back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     233         475 :                                         backType = stack.back().first = BackIsPlainList;
     234         475 :                                         parsePlainToken(back->arrayVal->back(), token);
     235        1300 :                                 } else if (r.empty() || r.is(')') || r.is(';')) {
     236        1300 :                                         parsePlainToken(*back, token);
     237        1300 :                                         pop();
     238             :                                 }
     239             :                         }
     240        1775 :                         break;
     241        2100 :                 case BackIsPlainList:
     242        2100 :                         if (r.is(')') || r.is(';')) {
     243         475 :                                 pop();
     244         475 :                                 continue;
     245        1625 :                         } else if (r.is('(') || r.is("~(") || r.is(",~(") || r.is(",(")) {
     246          25 :                                 if (r.is(',')) {
     247          25 :                                         ++ r;
     248             :                                 }
     249          25 :                                 if (back->_type == ValueType::Type::ARRAY) {
     250          25 :                                         back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     251          25 :                                         push(BackIsGeneric, &back->arrayVal->back());
     252             :                                 } else {
     253           0 :                                         log::error("DataSerenityDecoder", "Generic value can not be used as key");
     254           0 :                                         stop = true;
     255           0 :                                         break;
     256             :                                 }
     257          25 :                                 break;
     258        1600 :                         } else if (r.is(',') || r.is<TokenSpecials>() || r.is<CharGroupId::Alphanumeric>()) {
     259        1600 :                                 if (r.is(',')) {
     260        1600 :                                         ++ r;
     261        1600 :                                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     262             :                                 }
     263             : 
     264        1600 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     265        1600 :                                 StringView token = r.readUntil<StringView::Chars<'~', ':', ',', ';', '(', ')'>, StringView::CharGroup<CharGroupId::WhiteSpace>>();
     266        1600 :                                 if (r.is(':')) {
     267           0 :                                         auto tmp = token; tmp.skipChars<StringView::CharGroup<CharGroupId::Numbers>>();
     268           0 :                                         if (tmp.empty()) {
     269           0 :                                                 tmp = r.readUntil<StringView::Chars<'~', ',', ';', '(', ')'>, StringView::CharGroup<CharGroupId::WhiteSpace>>();
     270           0 :                                                 token = StringView(token.data(), token.size() + tmp.size());
     271             :                                         }
     272             :                                 }
     273        1600 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     274        1600 :                                 if (r.is(':')) {
     275           0 :                                         pop();
     276           0 :                                         if (backType == BackIsDict) {
     277           0 :                                                 push(BackIsPlain, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     278             :                                         } else {
     279           0 :                                                 log::error("DataSerenityDecoder", "Colon sequence within plain list is invalid");
     280           0 :                                                 stop = true;
     281           0 :                                                 break;
     282             :                                         }
     283        1600 :                                 } else if (token.empty()) {
     284             :                                         // do nothing
     285             :                                 }
     286        1600 :                                 if (back->_type == ValueType::Type::ARRAY) {
     287        1600 :                                         if (r.is('(')) {
     288           0 :                                                 key.clear(); string::urldecode(key, token);
     289           0 :                                                 transformToDict(*back);
     290           0 :                                                 push(BackIsGeneric, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     291        1600 :                                         } else if (r.is("~(")) {
     292           0 :                                                 back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     293           0 :                                                 push(BackIsGeneric, &back->arrayVal->back());
     294             :                                         } else {
     295        1600 :                                                 back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     296        1600 :                                                 parsePlainToken(back->arrayVal->back(), token);
     297             :                                         }
     298             :                                 } else {
     299           0 :                                         key.clear(); string::urldecode(key, token);
     300           0 :                                         if (r.is('(') || r.is("~(")) {
     301           0 :                                                 push(BackIsGeneric, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     302             :                                         } else {
     303           0 :                                                 back->dictVal->emplace(key, ValueType(true));
     304             :                                         }
     305             :                                 }
     306             :                         } else {
     307           0 :                                 log::error("DataSerenityDecoder", "Invalid token in plain list");
     308           0 :                                 stop = true;
     309           0 :                                 break;
     310             :                         }
     311        1600 :                         break;
     312           0 :                 case BackIsPlainStop:
     313           0 :                         if (r.is(')') || r.is(';')) {
     314           0 :                                 pop();
     315           0 :                                 continue;
     316           0 :                         } else if (r.is(',') || r.is("~(")) {
     317           0 :                                 typename ValueType::ArrayType arr;
     318           0 :                                 arr.emplace_back(move(*back));
     319           0 :                                 *back = ValueType(move(arr));
     320           0 :                                 backType = stack.back().first = BackIsPlainList;
     321           0 :                         } else if (r.is<TokenSpecials>() || r.is<CharGroupId::Alphanumeric>()) {
     322           0 :                                 pop();
     323           0 :                                 continue;
     324             :                         } else {
     325           0 :                                 log::error("DataSerenityDecoder", "Invalid token in plain stop");
     326           0 :                                 stop = true;
     327             :                         }
     328           0 :                         break;
     329         100 :                 case BackIsArray:
     330         100 :                         if (!r.is(')')) {
     331          75 :                                 if (r.is(';') || r.is(',')) {
     332          75 :                                         ++ r;
     333          75 :                                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     334             :                                 }
     335          75 :                                 if (r.is('(')) {
     336           0 :                                         back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     337           0 :                                         backType = stack.back().first = BackIsArray;
     338           0 :                                         push(BackIsGeneric, &back->arrayVal->back());
     339             :                                 } else {
     340          75 :                                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     341          75 :                                         StringView token = r.readUntil<StringView::Chars<'~', ':', ',', ';', '(', ')'>, StringView::CharGroup<CharGroupId::WhiteSpace>>();
     342          75 :                                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     343          75 :                                         if (token.empty()) {
     344             :                                                 // do nothing
     345          75 :                                         } else if (r.is(':')) {
     346          25 :                                                 key.clear(); string::urldecode(key, token);
     347          25 :                                                 transformToDict(*back);
     348          25 :                                                 backType = stack.back().first = BackIsDict;
     349          25 :                                                 push(BackIsPlain, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     350          50 :                                         } else if (r.is('(') || r.is("~(")) {
     351           0 :                                                 key.clear();
     352           0 :                                                 string::urldecode(key, token);
     353           0 :                                                 transformToDict(*back);
     354           0 :                                                 backType = stack.back().first = BackIsDict;
     355           0 :                                                 push(BackIsGeneric, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     356             :                                         } else {
     357          50 :                                                 back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     358          50 :                                                 parsePlainToken(back->arrayVal->back(), token);
     359             :                                                 // continue
     360             :                                         }
     361             :                                 }
     362             :                         } else {
     363          25 :                                 back->arrayVal->shrink_to_fit();
     364          25 :                                 pop();
     365             :                         }
     366         100 :                         break;
     367        2225 :                 case BackIsDict:
     368        2225 :                         if (!r.is(')')) {
     369        1075 :                                 if (r.is(';') || r.is(',')) {
     370        1050 :                                         ++ r;
     371             :                                 }
     372             : 
     373        1075 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     374        1075 :                                 if (r.is(')')) {
     375          25 :                                         pop();
     376          25 :                                         continue;
     377             :                                 }
     378             : 
     379        1050 :                                 if (!r.is<TokenSpecials>() && !r.is<StringView::CharGroup<CharGroupId::Alphanumeric>>()) {
     380           0 :                                         stop = true;
     381           0 :                                         log::error("DataSerenityDecoder", "Invalid key");
     382           0 :                                         break;
     383             :                                 }
     384             : 
     385        1050 :                                 StringView token = r.readUntil<StringView::Chars<'~', ':', ',', ';', '(', ')'>, StringView::CharGroup<CharGroupId::WhiteSpace>>();
     386        1050 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     387        1050 :                                 key.clear(); string::urldecode(key, token);
     388        1050 :                                 if (r.is(':')) {
     389         750 :                                         push(BackIsPlain, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     390         300 :                                 } else if (r.is('(') || r.is("~(")) {
     391         300 :                                         push(BackIsGeneric, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     392           0 :                                 } else if (r.is(';') || r.is(',')) {
     393           0 :                                         back->dictVal->emplace(key, ValueType(true));
     394             :                                 } else {
     395           0 :                                         stop = true;
     396           0 :                                         log::error("DataSerenityDecoder", "Invalid token in value");
     397           0 :                                         break;
     398             :                                 }
     399             :                         } else {
     400        1150 :                                 pop();
     401             :                         }
     402        2200 :                         break;
     403        1300 :                 case BackIsGeneric:
     404        1300 :                         if (r.is('(') || r.is("~(")) {
     405        1300 :                                 if (r.is('(')) {
     406        1275 :                                         ++ r;
     407          25 :                                 } else if (r.is("~(")) {
     408          25 :                                         r += 2;
     409             :                                 }
     410        1300 :                                 if (r.is('(')) {
     411           0 :                                         back->_type = ValueType::Type::ARRAY;
     412           0 :                                         back->arrayVal = new typename ValueType::ArrayType();
     413           0 :                                         back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     414           0 :                                         backType = stack.back().first = BackIsArray;
     415           0 :                                         push(BackIsGeneric, &back->arrayVal->back());
     416             :                                 } else {
     417        1300 :                                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     418        1300 :                                         StringView token = r.readUntil<StringView::Chars<'~', ':', ',', ';', '(', ')'>, StringView::CharGroup<CharGroupId::WhiteSpace>>();
     419        1300 :                                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     420        1300 :                                         if (token.empty()) {
     421           0 :                                                 pop();
     422        1300 :                                         } else if (r.is(')') || r.empty()) {
     423          50 :                                                 parsePlainToken(*back, token);
     424          50 :                                                 pop();
     425        1250 :                                         } else if (r.is(':')) {
     426        1000 :                                                 key.clear(); string::urldecode(key, token);
     427        1000 :                                                 back->_type = ValueType::Type::DICTIONARY;
     428        2000 :                                                 back->dictVal = new typename ValueType::DictionaryType();
     429        1000 :                                                 backType = stack.back().first = BackIsDict;
     430        1000 :                                                 push(BackIsPlain, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     431             : 
     432        1000 :                                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     433             : 
     434         250 :                                         } else if (r.is('(') || r.is("~(")) {
     435         200 :                                                 key.clear(); string::urldecode(key, token);
     436         200 :                                                 back->_type = ValueType::Type::DICTIONARY;
     437         400 :                                                 back->dictVal = new typename ValueType::DictionaryType();
     438         200 :                                                 backType = stack.back().first = BackIsDict;
     439         200 :                                                 push(BackIsGeneric, &back->dictVal->emplace(key, ValueType::Type::EMPTY).first->second);
     440          50 :                                         } else if (r.is(',') || r.is(';')) {
     441          50 :                                                 back->_type = ValueType::Type::ARRAY;
     442         100 :                                                 back->arrayVal = new typename ValueType::ArrayType();
     443          50 :                                                 back->arrayVal->emplace_back(ValueType::Type::EMPTY);
     444          50 :                                                 backType = stack.back().first = BackIsArray;
     445          50 :                                                 parsePlainToken(back->arrayVal->back(), token);
     446             :                                         }
     447             :                                 }
     448             :                         } else {
     449           0 :                                 log::error("DataSerenityDecoder", "Invalid token in plain stop: '", r.sub(16), "'");
     450             :                         }
     451             : 
     452        1300 :                         break;
     453             :                 }
     454        7500 :         } while (!r.empty() && !stack.empty() && !stop);
     455         775 : }
     456             : 
     457             : template <typename Interface>
     458         775 : auto read(StringView &n) -> ValueTemplate<Interface> {
     459         775 :         auto r = n;
     460         775 :         if (r.empty() || r == "null") {
     461           0 :                 return ValueTemplate<Interface>();
     462             :         }
     463             : 
     464         775 :         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     465         775 :         Decoder<Interface> dec(r);
     466         775 :         ValueTemplate<Interface> ret;
     467         775 :         dec.parse(ret);
     468         775 :         n = dec.r;
     469         775 :         return ret;
     470         775 : }
     471             : 
     472             : template <typename Interface>
     473         150 : auto read(const StringView &r) -> ValueTemplate<Interface> {
     474         150 :         StringView tmp(r);
     475         300 :         return read<Interface>(tmp);
     476             : }
     477             : 
     478             : }
     479             : 
     480             : #endif /* STAPPLER_DATA_SPDATADECODESERENITY_H_ */

Generated by: LCOV version 1.14