LCOV - code coverage report
Current view: top level - src/plugins/yajl - yajl_gen_open.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 65 66 98.5 %
Date: 2019-09-12 12:28:41 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #include "yajl_gen.h"
      10             : 
      11             : /**
      12             :  * @brief Iterate over string and open everything
      13             :  *
      14             :  * @pre Sometimes the first or last value needs special handling, the
      15             :  * caller needs to do that.
      16             :  * Implements lookahead for arrays, but does not implement leaf
      17             :  * semantics.
      18             :  *
      19             :  * @pre g must be in a map environment
      20             :  *
      21             :  * @post g is left in map or array environment
      22             :  *
      23             :  *
      24             :  * It handles following scenarios:
      25             :  *
      26             :  * (N0)
      27             :  * #/_
      28             :  * found array start, but followed by non-array so we need a map
      29             :  * yield array and map (name of array done already)
      30             :  *
      31             :  * (N1)
      32             :  * #
      33             :  * found array start, yield array (name done already)
      34             :  *
      35             :  * (N2)
      36             :  * _/# (or empty array/map)
      37             :  *
      38             :  * found array name, yield string (array done later)
      39             :  *
      40             :  * (N3)
      41             :  * _
      42             :  * found map start, yield string + map
      43             :  *
      44             :  * @param g to yield maps, strings
      45             :  * @param pnext a pointer to the name of the key at the correct pos
      46             :  * @param levels to iterate, if smaller or equal zero it does nothing
      47             :  */
      48         236 : static void elektraGenOpenIterate (yajl_gen g, const char * pnext, int levels)
      49             : {
      50         236 :         size_t size = 0;
      51             : 
      52             :         ELEKTRA_LOG_DEBUG ("levels: %d,  next: \"%s\"", levels, pnext);
      53             : 
      54         420 :         for (int i = 0; i < levels; ++i)
      55             :         {
      56         184 :                 pnext = keyNameGetOneLevel (pnext + size, &size);
      57             : 
      58         184 :                 lookahead_t lookahead = elektraLookahead (pnext, size);
      59             : 
      60             :                 ELEKTRA_LOG_DEBUG ("level by name %d: \"%.*s\", lookahead: %d", (int) i, (int) size, pnext, lookahead);
      61             : 
      62             :                 // do not yield array indizes as names
      63         184 :                 if (*pnext == '#')
      64             :                 {
      65             :                         ELEKTRA_LOG_DEBUG ("GEN (N1) array by name");
      66          54 :                         yajl_gen_array_open (g);
      67             : 
      68          54 :                         if (lookahead == LOOKAHEAD_MAP)
      69             :                         {
      70             :                                 ELEKTRA_LOG_DEBUG ("GEN (N0) anon-map");
      71          30 :                                 yajl_gen_map_open (g);
      72             :                         }
      73             :                 }
      74         130 :                 else if (lookahead == LOOKAHEAD_ARRAY || lookahead == LOOKAHEAD_EMPTY_ARRAY || lookahead == LOOKAHEAD_EMPTY_MAP)
      75             :                 {
      76             :                         ELEKTRA_LOG_DEBUG ("GEN (N2) string for start/empty array/map %.*s", (int) size, pnext);
      77          48 :                         yajl_gen_string (g, (const unsigned char *) pnext, size);
      78             : 
      79             :                         // opening (empty) array will be handled later
      80             :                 }
      81             :                 else
      82             :                 {
      83             :                         ELEKTRA_LOG_DEBUG ("GEN (N3) string %.*s", (int) size, pnext);
      84          82 :                         yajl_gen_string (g, (const unsigned char *) pnext, size);
      85             : 
      86             :                         ELEKTRA_LOG_DEBUG ("GEN (N3) map by name");
      87          82 :                         yajl_gen_map_open (g);
      88             :                 }
      89             :         }
      90         236 : }
      91             : 
      92             : 
      93             : /**
      94             :  * @brief fixes elektraGenOpenIterate for the special handling of
      95             :  * arrays at very last position.
      96             :  *
      97             :  * @param g generate array there
      98             :  * @param key the key to look at
      99             :  */
     100         236 : static void elektraGenOpenLast (yajl_gen g, const Key * key)
     101             : {
     102         236 :         keyNameReverseIterator last = elektraKeyNameGetReverseIterator (key);
     103         236 :         elektraKeyNameReverseNext (&last);
     104             : 
     105             :         ELEKTRA_LOG_DEBUG ("last startup entry: \"%.*s\"", (int) last.size, last.current);
     106             : 
     107         236 :         if (last.current[0] == '#' && strcmp (last.current, "###empty_array"))
     108             :         {
     109             :                 // is an array, but not an empty one
     110             :                 ELEKTRA_LOG_DEBUG ("GEN array open last");
     111          57 :                 yajl_gen_array_open (g);
     112             :         }
     113         236 : }
     114             : 
     115             : /**
     116             :  * @brief Open initially
     117             :  *
     118             :  * Unlike in elektraGenOpen there is no precondition that parentKey
     119             :  * must be below first. Instead:
     120             :  *
     121             :  * @pre first must be below parentKey
     122             :  *
     123             :  * @pre g expects to be in a map
     124             :  *
     125             :  * @post g left in map or array
     126             :  *
     127             :  * Does not have special handling for first key (only for last key)
     128             :  *
     129             :  * @see elektraGenOpen
     130             :  *
     131             :  * @param g
     132             :  * @param parentKey
     133             :  * @param first
     134             :  */
     135          66 : void elektraGenOpenInitial (yajl_gen g, Key * parentKey, const Key * first)
     136             : {
     137          66 :         const char * pfirst = keyName (first);
     138          66 :         size_t csize = 0;
     139             : 
     140          66 :         int equalLevels = elektraKeyCountEqualLevel (parentKey, first);
     141          66 :         int firstLevels = elektraKeyCountLevel (first);
     142             : 
     143             :         // forward all equal levels
     144         334 :         for (int i = 0; i < equalLevels + 1; ++i)
     145             :         {
     146         268 :                 pfirst = keyNameGetOneLevel (pfirst + csize, &csize);
     147             :         }
     148             : 
     149             :         // calculate levels: do not iterate over last element
     150          66 :         const int levelsToOpen = firstLevels - equalLevels - 1;
     151             : 
     152             :         ELEKTRA_LOG_DEBUG ("open by name Initial %s, equal: %d, to open: %d", pfirst, equalLevels, levelsToOpen);
     153             : 
     154             : 
     155          66 :         if (pfirst && *pfirst == '#')
     156             :         {
     157             :                 ELEKTRA_LOG_DEBUG ("array open INITIAL");
     158             :         }
     159             :         else
     160             :         {
     161             :                 ELEKTRA_LOG_DEBUG ("GEN map open INITIAL");
     162          60 :                 yajl_gen_map_open (g);
     163             :         }
     164             : 
     165             : 
     166          66 :         elektraGenOpenIterate (g, pfirst, levelsToOpen);
     167             : 
     168          66 :         elektraGenOpenLast (g, first);
     169          66 : }
     170             : 
     171             : 
     172             : /**
     173             :  * @brief Implements special handling for the first found name
     174             :  *
     175             :  * @pre expects to be in a map or array
     176             :  *
     177             :  * @post will leave in a map or array
     178             :  *
     179             :  * Looks at first place where two key name differ and need a lookahead
     180             :  * of one in next to not generate too much.
     181             :  *
     182             :  * It handles the first level entirely, except of values.
     183             :  *
     184             :  * _ .. is an arbitrary map
     185             :  * # .. is an arbitrary member of array
     186             :  * Note that "test/" would not be contained in the string
     187             :  * given by argument.
     188             :  *
     189             :  * (O1)
     190             :  * cur:  test/#
     191             :  * next: test/#
     192             :  *
     193             :  * Will do nothing, because we are in a situation where we just iterate
     194             :  * over leaves in the array.
     195             :  *
     196             :  * (O2)
     197             :  * cur:  test/#
     198             :  * next: test/#/#
     199             :  *
     200             :  * Will create an array in the array.
     201             :  * Array won't have a name.
     202             :  * Staying in the same array "test".
     203             :  *
     204             :  * (O3)
     205             :  * cur:  test/#
     206             :  * next: test/#/_
     207             :  *
     208             :  * Will yield a map (name will be yield later).
     209             :  * Staying in the same array.
     210             :  *
     211             :  * (O4)
     212             :  * cur:  test/_
     213             :  * next: test/_
     214             :  *
     215             :  * Will yield a value. (Value yield later)
     216             :  *
     217             :  * (O5)
     218             :  * cur:  test/_
     219             :  * next: test/_/# (or empty map/array)
     220             :  *
     221             :  * Will yield the name of the array (array handled later)
     222             :  *
     223             :  * (O6s O6m O6e)
     224             :  * cur:  test/_
     225             :  * next: test/_/_
     226             :  *
     227             :  * Will yield the name (O6s) of the map and the map (O6m)
     228             :  * if it is an empty map, do not yield a map (O6e)
     229             :  *
     230             :  * @pre
     231             :  * (P1) Precondition
     232             :  * cur:  test/_
     233             :  * next: test
     234             :  *
     235             :  * Is not possible.
     236             :  * @see elektraNextNotBelow
     237             :  *
     238             :  * @pre
     239             :  * (P2) Precondition
     240             :  * cur:  test
     241             :  * next: test/_
     242             :  *
     243             :  * Is not possible.
     244             :  * @see elektraGenOpenInitial
     245             :  *
     246             :  * (E1)
     247             :  * cur:  test/_
     248             :  * next: test/#
     249             :  *
     250             :  * Error situation: cannot change to array within map.
     251             :  * Lookahead does not matter.
     252             :  *
     253             :  * (E2)
     254             :  * cur:  test/#
     255             :  * next: test/_
     256             :  *
     257             :  * Error situation: leaving array in the middle.
     258             :  * Lookahead does not matter.
     259             :  *
     260             :  * Rationale for arrays:
     261             :  * Error situations should be handled by struct checker.
     262             :  * They could be avoided by encoding array names like
     263             :  * array#0, but this would lead to the possibility to create
     264             :  * non-unique names which would be kicked away in json.
     265             :  * It is considered that always generating correct files
     266             :  * is more important than not having the possibility to generate
     267             :  * wrong keysets for a particual storage.
     268             :  *
     269             :  * @param g
     270             :  * @param cur
     271             :  * @param next
     272             :  */
     273         170 : static void elektraGenOpenFirst (yajl_gen g, const char * cur, const char * next, size_t nextSize)
     274             : {
     275         170 :         lookahead_t lookahead = elektraLookahead (next, nextSize);
     276             :         ELEKTRA_LOG_DEBUG ("cur: \"%s\" next: \"%s\", lookahead: %d", cur, next, lookahead);
     277             : 
     278         170 :         if (*cur == '#')
     279             :         {
     280          68 :                 if (*next == '#')
     281             :                 {
     282          68 :                         if (lookahead == LOOKAHEAD_MAP)
     283             :                         {
     284             :                                 ELEKTRA_LOG_DEBUG ("GEN (O3) next anon map");
     285          26 :                                 yajl_gen_map_open (g);
     286             :                         }
     287             :                         else
     288             :                         {
     289             :                                 ELEKTRA_LOG_DEBUG ("we are iterating over array, nothing to do");
     290             :                         }
     291             :                 }
     292             :                 else
     293             :                 {
     294             :                         ELEKTRA_LOG_DEBUG ("ERROR should not happen");
     295             :                 }
     296             :         }
     297             :         else
     298             :         {
     299         102 :                 if (lookahead == LOOKAHEAD_END)
     300             :                 {
     301             :                         ELEKTRA_LOG_DEBUG ("GEN string (O4)");
     302           0 :                         yajl_gen_string (g, (const unsigned char *) next, nextSize);
     303             :                 }
     304         102 :                 else if (lookahead == LOOKAHEAD_ARRAY || lookahead == LOOKAHEAD_EMPTY_ARRAY || lookahead == LOOKAHEAD_EMPTY_MAP)
     305             :                 {
     306             :                         ELEKTRA_LOG_DEBUG ("GEN string for start/empty array (O5)");
     307          45 :                         yajl_gen_string (g, (const unsigned char *) next, nextSize);
     308             :                         // opening (empty) array will be handled later
     309             :                 }
     310          57 :                 else if (lookahead == LOOKAHEAD_MAP)
     311             :                 {
     312             :                         ELEKTRA_LOG_DEBUG ("GEN string (O6s)");
     313          57 :                         yajl_gen_string (g, (const unsigned char *) next, nextSize);
     314             : 
     315             :                         ELEKTRA_LOG_DEBUG ("GEN map (O6m)");
     316          57 :                         yajl_gen_map_open (g);
     317             :                 }
     318             :         }
     319         170 : }
     320             : 
     321             : /**
     322             :  * @brief open so many levels as needed for key next
     323             :  *
     324             :  * Iterates over next and generate
     325             :  * needed groups for every name/ and needed arrays
     326             :  * for every name/#.
     327             :  *
     328             :  * Yields names for leafs, but suppresses to yield names
     329             :  * for array entries (like #0)
     330             :  *
     331             :  * @see elektraGenOpenFirst
     332             :  *
     333             :  * @pre keys are not allowed to be below
     334             :  *
     335             :  * @example
     336             :  *
     337             :  * Example for elektraNextNotBelow:
     338             :  * cur:  user/sw/org
     339             :  * next: user/sw/org/deeper
     340             :  * -> do nothing, "deeper" is value
     341             :  *
     342             :  *  -- cut --
     343             :  *
     344             :  * cur:  user/sw/org/deeper
     345             :  * next: user/sw/org/other
     346             :  * -> this cannot happen (see elektraNextNotBelow)
     347             :  *
     348             :  * cur:  user/sw/org/other
     349             :  * next: user/sw/org/other/deeper/below
     350             :  * -> this cannot happen (see elektraNextNotBelow)
     351             :  *
     352             :  *  -- cut --
     353             :  *
     354             :  * instead of cut two entries above following would happen:
     355             :  * cur:  user/sw/org/deeper
     356             :  * next: user/sw/org/other/deeper/below
     357             :  * -> and "other" and "deeper" would be opened
     358             :  *
     359             :  * cur:  user/sw/org/other/deeper/below
     360             :  * next: user/no
     361             :  * -> do nothing, because "no" is value
     362             :  *
     363             :  * cur:  user/no
     364             :  * next: user/oops/it/is/below
     365             :  * -> create map "oops" "it" "is"
     366             :  *
     367             :  * cur:  user/oops/it/is/below
     368             :  * next: user/x/t/s/x
     369             :  * -> create "x" "t" "s"
     370             :  *
     371             :  * @example
     372             :  *
     373             :  * cur:  user/sw/org/#0
     374             :  * next: user/sw/org/#1
     375             :  * -> will not open org or array (because that did not change),
     376             :  *    but will open group test (because within arrays every key
     377             :  *    needs a group).
     378             :  *
     379             :  * cur:  user/sw/org/#0
     380             :  * next: user/sw/oth/#0
     381             :  * -> will open new group oth and new array and yield blah
     382             :  *
     383             :  * cur:  user/sw
     384             :  * next: user/sw/array/#0
     385             :  * -> will yield a new array using name "array"
     386             :  *
     387             :  * @pre cur and next have a name which is not equal
     388             :  *
     389             :  * @param g handle to generate to
     390             :  * @param cur current key of iteration
     391             :  * @param next next key of iteration
     392             :  */
     393         777 : void elektraGenOpen (yajl_gen g, const Key * cur, const Key * next)
     394             : {
     395         777 :         const char * pcur = keyName (cur);
     396         777 :         const char * pnext = keyName (next);
     397             :         // size_t curLevels = elektraKeyCountLevel(cur);
     398         777 :         size_t nextLevels = elektraKeyCountLevel (next);
     399         777 :         size_t size = 0;
     400         777 :         size_t csize = 0;
     401             : 
     402         777 :         size_t equalLevels = elektraKeyCountEqualLevel (cur, next);
     403             : 
     404             :         // forward all equal levels
     405        6590 :         for (size_t i = 0; i < equalLevels + 1; ++i)
     406             :         {
     407        5813 :                 pnext = keyNameGetOneLevel (pnext + size, &size);
     408        5813 :                 pcur = keyNameGetOneLevel (pcur + csize, &csize);
     409             :         }
     410             : 
     411             :         // always skip first and last level
     412         777 :         const int levelsToSkip = 2;
     413             : 
     414             :         // calculate levels which are neither already handled
     415             :         // nor the last one
     416         777 :         int levels = (int) nextLevels - (int) (equalLevels + levelsToSkip);
     417             : 
     418         777 :         int actionRequired = equalLevels + 1 < nextLevels;
     419             : 
     420             :         ELEKTRA_LOG_DEBUG ("%d: pcur: %s , pnext: %s, action: %d", (int) levels, pcur, pnext, actionRequired);
     421             : 
     422             :         // check if anything needs to be done at all
     423         777 :         if (actionRequired)
     424             :         {
     425         170 :                 elektraGenOpenFirst (g, pcur, pnext, size);
     426             : 
     427             :                 // skip the first level we did already
     428         170 :                 pnext = keyNameGetOneLevel (pnext + size, &size);
     429             : 
     430             :                 // now yield everything else in the string but the last value
     431         170 :                 elektraGenOpenIterate (g, pnext, levels);
     432             : 
     433         170 :                 elektraGenOpenLast (g, next);
     434             :         }
     435         777 : }

Generated by: LCOV version 1.13