LCOV - code coverage report
Current view: top level - src/plugins/ccode - coder.cpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 85 85 100.0 %
Date: 2019-09-12 12:28:41 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Implementation of string encoding and decoding class
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include <vector>
      11             : 
      12             : #include "coder.hpp"
      13             : 
      14             : namespace
      15             : {
      16             : /**
      17             :  * @brief This function maps hex characters to integer numbers.
      18             :  *
      19             :  * @pre The specified character has to be between
      20             :  *
      21             :  *      - `'0'`–`'9'`,
      22             :  *      - `'a'`-`'f'`, or
      23             :  *      - `'A'`-`'F'`
      24             :  *
      25             :  *     .
      26             :  *
      27             :  * @param character This argument specifies the (hexadecimal) character this function converts.
      28             :  *
      29             :  * @return An integer number between `0` and `15` if the precondition is valid or `0` otherwise
      30             :  */
      31             : int elektraHexcodeConvFromHex (char const character)
      32             : {
      33        3962 :         if (character >= '0' && character <= '9') return character - '0';
      34        1015 :         if (character >= 'a' && character <= 'f') return character - 'a' + 10;
      35         984 :         if (character >= 'A' && character <= 'F') return character - 'A' + 10;
      36             : 
      37             :         return 0; /* Unknown escape char */
      38             : }
      39             : } // end namespace
      40             : 
      41             : namespace elektra
      42             : {
      43             : using CppKeySet = kdb::KeySet;
      44             : using CppKey = kdb::Key;
      45             : 
      46             : /**
      47             :  * @brief This function sets default values for the encoding and decoding character mapping.
      48             :  */
      49         149 : void Coder::setDefaultConfig ()
      50             : {
      51             :         unsigned char pairs[][2] = { { '\b', 'b' }, { '\t', 't' },  { '\n', 'n' },  { '\v', 'v' }, { '\f', 'f' },
      52         149 :                                      { '\r', 'r' }, { '\\', '\\' }, { '\'', '\'' }, { '\"', '"' }, { '\0', '0' } };
      53             : 
      54        1639 :         for (size_t pair = 0; pair < sizeof (pairs) / sizeof (pairs[0]); pair++)
      55             :         {
      56        1490 :                 unsigned char character = pairs[pair][0];
      57        1490 :                 unsigned char replacement = pairs[pair][1];
      58             : 
      59        2980 :                 encode[character] = replacement;
      60        2980 :                 decode[replacement] = character;
      61             :         }
      62         149 : }
      63             : 
      64             : /**
      65             :  * @brief This function sets values for the encoding and decoding character mapping.
      66             :  *
      67             :  * @param config This key set stores configuration values for the character mapping.
      68             :  * @param root This key stores the root key for the character mapping stored in `config`.
      69             :  */
      70         321 : void Coder::readConfig (CppKeySet const & config, CppKey const & root)
      71             : {
      72        3329 :         for (auto key : config)
      73             :         {
      74             :                 /* Ignore keys that are not directly below the config root key or have an incorrect size */
      75        4132 :                 if (!key->isDirectBelow (root) || key->getBaseNameSize () != 3 || key->getBinarySize () != 3) continue;
      76             : 
      77        4415 :                 int character = elektraHexcodeConvFromHex (key->getBaseName ()[1]);
      78        4415 :                 character += elektraHexcodeConvFromHex (key->getBaseName ()[0]) * 16;
      79             : 
      80        4415 :                 int replacement = elektraHexcodeConvFromHex (key->get<string> ()[1]);
      81        4415 :                 replacement += elektraHexcodeConvFromHex (key->get<string> ()[0]) * 16;
      82             : 
      83             :                 /* Hexencode this character! */
      84        1766 :                 encode[character & 255] = replacement;
      85        1766 :                 decode[replacement & 255] = character;
      86             :         }
      87         321 : }
      88             : 
      89             : /**
      90             :  * @brief This function replaces unescaped characters in a string with escaped characters.
      91             :  *
      92             :  * @param text This variable stores the string this function escapes.
      93             :  *
      94             :  * @return The encoded string
      95             :  */
      96         253 : string Coder::encodeString (string const & text)
      97             : {
      98         506 :         vector<unsigned char> encoded;
      99             : 
     100        2401 :         for (unsigned char character : text)
     101             :         {
     102        2778 :                 if (encode[character])
     103             :                 {
     104          84 :                         encoded.push_back (escapeCharacter);
     105         168 :                         encoded.push_back (encode[character]);
     106             :                 }
     107             :                 else
     108             :                 {
     109        1305 :                         encoded.push_back (character);
     110             :                 }
     111             :         }
     112             : 
     113        1518 :         return { encoded.begin (), encoded.end () };
     114             : }
     115             : 
     116             : /**
     117             :  * @brief This function replaces escaped characters in a string with unescaped characters.
     118             :  *
     119             :  * @param text This string stores the string this function decodes.
     120             :  *
     121             :  * @return The decoded string
     122             :  */
     123         626 : string Coder::decodeString (string const & text)
     124             : {
     125        1252 :         vector<char> decoded;
     126             : 
     127         626 :         auto character = text.begin ();
     128             : 
     129        8466 :         while (character != text.end ())
     130             :         {
     131        3607 :                 if (*character == escapeCharacter)
     132             :                 {
     133         131 :                         ++character;
     134         393 :                         decoded.push_back (decode[*character]);
     135             :                 }
     136             :                 else
     137             :                 {
     138        3476 :                         decoded.push_back (*character);
     139             :                 }
     140             :                 ++character;
     141             :         }
     142             : 
     143        3756 :         return { decoded.begin (), decoded.end () };
     144             : }
     145             : 
     146             : /**
     147             :  * @brief This function replaces unescaped characters in a key value with escaped characters.
     148             :  *
     149             :  * The function only modifies the key value if it has type `string`.
     150             :  *
     151             :  * @param key This key stores the value this function encodes.
     152             :  */
     153          59 : void Coder::encodeValue (CppKey & key)
     154             : {
     155         279 :         if (key.isString ()) key.setString (encodeString (key.get<string> ()));
     156          59 : }
     157             : 
     158             : /**
     159             :  * @brief This function replaces escaped characters in a key value with unescaped characters.
     160             :  *
     161             :  * The function only modifies the key value if it has type `string`.
     162             :  *
     163             :  * @param key This key holds the value this function decodes.
     164             :  */
     165         143 : void Coder::decodeValue (CppKey & key)
     166             : {
     167         667 :         if (key.isString ()) key.setString (decodeString (key.get<string> ()));
     168         143 : }
     169             : 
     170             : /**
     171             :  * @brief This function replaces unescaped characters in a key name with escaped characters.
     172             :  *
     173             :  * @param key This `Key` stores a name possibly containing unescaped special characters.
     174             :  *
     175             :  * @return A copy of `key` containing an escaped version of the name of `key`
     176             :  */
     177          59 : CppKey Coder::encodeName (CppKey const & key)
     178             : {
     179         118 :         CppKey escaped{ key.dup () };
     180         118 :         escaped.setName (key.getNamespace ());
     181             :         auto keyIterator = key.begin ();
     182             : 
     183         969 :         while (++keyIterator != key.end ())
     184             :         {
     185         594 :                 escaped.addBaseName (encodeString (*keyIterator));
     186             :         }
     187             :         ELEKTRA_LOG_DEBUG ("Encoded name of “%s” is “%s”", key.getName ().c_str (), escaped.getName ().c_str ());
     188          59 :         return escaped;
     189             : }
     190             : 
     191             : /**
     192             :  * @brief This function replaces escaped characters in a key name with unescaped characters.
     193             :  *
     194             :  * @param key This `Key` stores a name possibly containing escaped special characters.
     195             :  *
     196             :  * @return A copy of `key` containing an unescaped version of the name of `key`
     197             :  */
     198         143 : CppKey Coder::decodeName (CppKey const & key)
     199             : {
     200         286 :         CppKey unescaped{ key.dup () };
     201         286 :         unescaped.setName (key.getNamespace ());
     202             :         auto keyIterator = key.begin ();
     203             : 
     204        2409 :         while (++keyIterator != key.end ())
     205             :         {
     206        1485 :                 unescaped.addBaseName (decodeString (*keyIterator));
     207             :         }
     208             :         ELEKTRA_LOG_DEBUG ("Decoded name of “%s” is “%s”", key.getName ().c_str (), unescaped.getName ().c_str ());
     209         143 :         return unescaped;
     210             : }
     211             : 
     212             : /**
     213             :  * @brief This constructor creates a new object used to decode and encode key sets.
     214             :  *
     215             :  * @param config This key set contains configuration values for decoding and encoding such as mapping data and the escape character.
     216             :  */
     217        1410 : Coder::Coder (CppKeySet config)
     218             : {
     219        1880 :         encode = vector<unsigned char> (256);
     220        1880 :         decode = vector<unsigned char> (256);
     221             : 
     222         470 :         escapeCharacter = '\\';
     223        2820 :         CppKey const escape = config.lookup ("/escape", 0);
     224         900 :         if (escape && escape.getBaseNameSize () && escape.getBinarySize () == 3)
     225             :         {
     226         860 :                 int escapeChar = elektraHexcodeConvFromHex (escape.get<string> ()[1]);
     227         860 :                 escapeChar += elektraHexcodeConvFromHex (escape.get<string> ()[0]) * 16;
     228             : 
     229         215 :                 escapeCharacter = escapeChar & 255;
     230             :         }
     231             :         ELEKTRA_LOG_DEBUG ("Use “%c” as escape character", escapeCharacter);
     232             : 
     233         940 :         CppKey const root = CppKey{ "/chars", KEY_END };
     234        1880 :         CppKeySet mappingConfig{ config.cut (root) };
     235             : 
     236             : #ifdef HAVE_LOGGER
     237             :         for (auto key : mappingConfig)
     238             :         {
     239             :                 ELEKTRA_LOG_DEBUG ("Decode 0x%s as 0x%s ", key->getBaseName ().c_str (), key.getString ().c_str ());
     240             :         }
     241             : #endif
     242             : 
     243         470 :         if (mappingConfig.size () > 0)
     244             :         {
     245         321 :                 readConfig (mappingConfig, root);
     246             :         }
     247             :         else
     248             :         {
     249         149 :                 setDefaultConfig ();
     250             :         }
     251         470 : }
     252             : 
     253             : /**
     254             :  * @brief This function escapes special characters in key names and values.
     255             :  *
     256             :  * @param keys This key set stores keys that possibly contain special characters.
     257             :  *
     258             :  * @return A copy of the given KeySet containing only escaped keys
     259             :  */
     260          46 : CppKeySet Coder::encodeKeySet (CppKeySet const & keys)
     261             : {
     262          46 :         CppKeySet escaped{};
     263         315 :         for (auto key : keys)
     264             :         {
     265         236 :                 CppKey encoded = encodeName (*key);
     266          59 :                 encodeValue (encoded);
     267          59 :                 escaped.append (encoded);
     268             :         }
     269          46 :         return escaped;
     270             : }
     271             : 
     272             : /**
     273             :  * @brief This function unescapes characters in key names and values.
     274             :  *
     275             :  * @param keys This key set stores keys that possibly contain escaped special characters.
     276             :  *
     277             :  * @return A copy of the given KeySet containing unescaped keys
     278             :  */
     279          89 : CppKeySet Coder::decodeKeySet (CppKeySet const & keys)
     280             : {
     281          89 :         CppKeySet unescaped{};
     282         696 :         for (auto key : keys)
     283             :         {
     284         572 :                 CppKey decoded = decodeName (*key);
     285         143 :                 decodeValue (decoded);
     286         143 :                 unescaped.append (decoded);
     287             :         }
     288             : 
     289          89 :         return unescaped;
     290             : }
     291             : 
     292             : } // end namespace elektra

Generated by: LCOV version 1.13