1 // ================================================================
2 // Array-only (open addressing) multi-level hash map, with linear probing for collisions.
3 // All keys, and terminal-level values, are mlrvals.
4 //
5 // Notes:
6 // * null key is not supported.
7 // * null value is not supported.
8 //
9 // See also:
10 // * http://en.wikipedia.org/wiki/Hash_table
11 // * http://docs.oracle.com/javase/6/docs/api/java/util/Map.html
12 // ================================================================
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "lib/mlr_globals.h"
19 #include "lib/mlrutil.h"
20 #include "containers/mlhmmv.h"
21 #include "lib/mvfuncs.h"
22
23 // ================================================================
24 // Allow compile-time override, e.g using gcc -D.
25
26 #ifndef LOAD_FACTOR
27 #define LOAD_FACTOR 0.7
28 #endif
29
30 #ifndef ENLARGEMENT_FACTOR
31 #define ENLARGEMENT_FACTOR 2
32 #endif
33
34 #define OCCUPIED 0xa4
35 #define DELETED 0xb8
36 #define EMPTY 0xce
37
38 // ================================================================
39 static int mlhmmv_hash_func(mv_t* plevel_key);
40 static void json_decimal_print (FILE* ostream, char* s, double parsed);
41 static void json_print_string_escaped(FILE* ostream, char* s);
42
43 // ----------------------------------------------------------------
44 static void mlhmmv_level_init(mlhmmv_level_t *plevel, int length);
45
46 // ----------------------------------------------------------------
47 static int mlhmmv_level_find_index_for_key(mlhmmv_level_t* plevel, mv_t* plevel_key, int* pideal_index);
48
49 static mlhmmv_level_entry_t* mlhmmv_level_look_up_and_ref_entry(
50 mlhmmv_level_t* plevel, sllmve_t* prestkeys, int* perror);
51
52 static mlhmmv_level_entry_t* mlhmmv_level_get_next_level_entry(mlhmmv_level_t* plevel, mv_t* plevel_key, int* pindex);
53 static mlhmmv_xvalue_t* mlhmmv_level_get_next_level_xvalue(mlhmmv_level_t* plevel, mv_t* plevel_key);
54
55 static mlhmmv_level_t* mlhmmv_level_ref_or_create(mlhmmv_level_t* plevel, sllmve_t* prest_keys);
56 static mlhmmv_level_t* mlhmmv_level_get_or_create_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys);
57
58 static void mlhmmv_level_enlarge(mlhmmv_level_t* plevel);
59 static void mlhmmv_level_move(mlhmmv_level_t* plevel, mv_t* plevel_key, mlhmmv_xvalue_t* plevel_value);
60
61 static void mlhmmv_level_put_xvalue_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mlhmmv_xvalue_t* pvalue);
62 static void mlhmmv_level_put_terminal_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mv_t* pterminal_value);
63
64 static void mlhmmv_level_to_lrecs_across_records(
65 mlhmmv_level_t* plevel,
66 char* prefix,
67 sllmve_t* prestnames,
68 lrec_t* ptemplate,
69 sllv_t* poutrecs,
70 int do_full_prefixing,
71 char* flatten_separator);
72
73 static void mlhmmv_level_to_lrec_within_record(
74 mlhmmv_level_t* plevel,
75 char* prefix,
76 lrec_t* poutrec,
77 int do_full_prefixing,
78 char* flatten_separator);
79
80 static void mlhhmv_levels_to_lrecs_lashed_across_records(
81 mlhmmv_level_t** plevels,
82 char** prefixes,
83 int num_levels,
84 sllmve_t* prestnames,
85 lrec_t* ptemplate,
86 sllv_t* poutrecs,
87 int do_full_prefixing,
88 char* flatten_separator);
89
90 static void mlhhmv_levels_to_lrecs_lashed_within_records(
91 mlhmmv_level_t** pplevels,
92 char** prefixes,
93 int num_levels,
94 lrec_t* poutrec,
95 int do_full_prefixing,
96 char* flatten_separator);
97
98 static void mlhmmv_level_print_single_line(mlhmmv_level_t* plevel, int depth,
99 int do_final_comma, int quote_keys_always, int quote_values_always,
100 FILE* ostream);
101
102 // ----------------------------------------------------------------
103 static void mlhmmv_root_put_xvalue(mlhmmv_root_t* pmap, sllmv_t* pmvkeys, mlhmmv_xvalue_t* pvalue);
104
105 // ================================================================
106 typedef int mlhmmv_typed_hash_func(mv_t* pa);
mlhmmv_string_hash_func(mv_t * pa)107 static int mlhmmv_string_hash_func(mv_t* pa) {
108 return mlr_string_hash_func(pa->u.strv);
109 }
mlhmmv_int_hash_func(mv_t * pa)110 static int mlhmmv_int_hash_func(mv_t* pa) {
111 return pa->u.intv;
112 }
mlhmmv_other_hash_func(mv_t * pa)113 static int mlhmmv_other_hash_func(mv_t* pa) {
114 fprintf(stderr, "%s: map keys must be of type %s or %s; got %s.\n",
115 MLR_GLOBALS.bargv0,
116 mt_describe_type(MT_STRING),
117 mt_describe_type(MT_INT),
118 mt_describe_type(pa->type));
119 exit(1);
120 }
121 static mlhmmv_typed_hash_func* mlhmmv_hash_func_dispositions[MT_DIM] = {
122 /*ERROR*/ mlhmmv_other_hash_func,
123 /*ABSENT*/ mlhmmv_other_hash_func,
124 /*EMPTY*/ mlhmmv_other_hash_func,
125 /*STRING*/ mlhmmv_string_hash_func,
126 /*INT*/ mlhmmv_int_hash_func,
127 /*FLOAT*/ mlhmmv_other_hash_func,
128 /*BOOL*/ mlhmmv_other_hash_func,
129 };
mlhmmv_hash_func(mv_t * pa)130 static int mlhmmv_hash_func(mv_t* pa) {
131 return mlhmmv_hash_func_dispositions[pa->type](pa);
132 }
133
134 // ================================================================
mlhmmv_print_terminal(mv_t * pmv,int quote_keys_always,int quote_values_always,FILE * ostream)135 void mlhmmv_print_terminal(mv_t* pmv, int quote_keys_always, int quote_values_always,
136 FILE* ostream)
137 {
138 char* level_value_string = mv_alloc_format_val(pmv);
139 if (quote_values_always) {
140 json_print_string_escaped(ostream, level_value_string);
141 } else if (pmv->type == MT_STRING) {
142 double parsed;
143 if (mlr_try_float_from_string(level_value_string, &parsed)) {
144 json_decimal_print(ostream, level_value_string, parsed);
145 } else if (streq(level_value_string, "true") || streq(level_value_string, "false")) {
146 fprintf(ostream, "%s", level_value_string);
147 } else {
148 json_print_string_escaped(ostream, level_value_string);
149 }
150 } else {
151 fprintf(ostream, "%s", level_value_string);
152 }
153 free(level_value_string);
154 }
155
156 // 0.123 is valid JSON; .123 is not. Meanwhile Miller is a format-converter tool
157 // so if there is perfectly legitimate CSV/DKVP/etc. data to be JSON-formatted,
158 // we make it JSON-compliant.
159 //
160 // Precondition: the caller has already checked that the string represents a number.
json_decimal_print(FILE * ostream,char * s,double parsed)161 static void json_decimal_print(FILE* ostream, char* s, double parsed) {
162 if (s[0] == '.') {
163 fprintf(ostream, "0%s", s);
164 } else if (s[0] == '-' && s[1] == '.') {
165 fprintf(ostream, "-0.%s", &s[2]);
166 } else {
167 fprintf(ostream, "%s", s);
168 }
169 }
170
json_print_string_escaped(FILE * ostream,char * s)171 static void json_print_string_escaped(FILE* ostream, char* s) {
172 fputc('"', ostream);
173 for (char* p = s; *p; p++) {
174 char c = *p;
175 switch (c) {
176 case '"':
177 fputc('\\', ostream);
178 fputc('"', ostream);
179 break;
180 case '\\':
181 fputc('\\', ostream);
182 fputc('\\', ostream);
183 break;
184 case '\n':
185 fputc('\\', ostream);
186 fputc('n', ostream);
187 break;
188 case '\r':
189 fputc('\\', ostream);
190 fputc('r', ostream);
191 break;
192 case '\t':
193 fputc('\\', ostream);
194 fputc('t', ostream);
195 break;
196 case '\b':
197 fputc('\\', ostream);
198 fputc('b', ostream);
199 break;
200 case '\f':
201 fputc('\\', ostream);
202 fputc('f', ostream);
203 break;
204 default:
205 fputc(c, ostream);
206 break;
207 }
208 }
209 fputc('"', ostream);
210 }
211
212 // ================================================================
mlhmmv_xvalue_alloc_empty_map()213 mlhmmv_xvalue_t mlhmmv_xvalue_alloc_empty_map() {
214 mlhmmv_xvalue_t xval = (mlhmmv_xvalue_t) {
215 .is_terminal = FALSE,
216 .terminal_mlrval= mv_absent(),
217 .pnext_level = mlhmmv_level_alloc()
218 };
219 return xval;
220 }
221
222 // ----------------------------------------------------------------
mlhmmv_xvalue_reset(mlhmmv_xvalue_t * pxvalue)223 void mlhmmv_xvalue_reset(mlhmmv_xvalue_t* pxvalue) {
224 pxvalue->is_terminal = TRUE;
225 pxvalue->terminal_mlrval = mv_absent();
226 pxvalue->pnext_level = NULL;
227 }
228
229 // ----------------------------------------------------------------
mlhmmv_xvalue_copy(mlhmmv_xvalue_t * pvalue)230 mlhmmv_xvalue_t mlhmmv_xvalue_copy(mlhmmv_xvalue_t* pvalue) {
231 if (pvalue->is_terminal) {
232 return (mlhmmv_xvalue_t) {
233 .is_terminal = TRUE,
234 .terminal_mlrval = mv_copy(&pvalue->terminal_mlrval),
235 .pnext_level = NULL,
236 };
237
238 } else {
239 mlhmmv_level_t* psrc_level = pvalue->pnext_level;
240 mlhmmv_level_t* pdst_level = mlr_malloc_or_die(sizeof(mlhmmv_level_t));
241
242 mlhmmv_level_init(pdst_level, MLHMMV_INITIAL_ARRAY_LENGTH);
243
244 for (
245 mlhmmv_level_entry_t* psubentry = psrc_level->phead;
246 psubentry != NULL;
247 psubentry = psubentry->pnext)
248 {
249 mlhmmv_xvalue_t next_value = mlhmmv_xvalue_copy(&psubentry->level_xvalue);
250 mlhmmv_level_put_xvalue_singly_keyed(pdst_level, &psubentry->level_key, &next_value);
251 }
252
253 return (mlhmmv_xvalue_t) {
254 .is_terminal = FALSE,
255 .terminal_mlrval = mv_absent(),
256 .pnext_level = pdst_level,
257 };
258 }
259 }
260
261 // ----------------------------------------------------------------
mlhmmv_xvalue_free(mlhmmv_xvalue_t * pxvalue)262 void mlhmmv_xvalue_free(mlhmmv_xvalue_t* pxvalue) {
263 if (pxvalue->is_terminal) {
264 mv_free(&pxvalue->terminal_mlrval);
265 } else if (pxvalue->pnext_level != NULL) {
266 mlhmmv_level_free(pxvalue->pnext_level);
267 pxvalue->pnext_level = NULL;
268 }
269 }
270
271 // ----------------------------------------------------------------
mlhmmv_xvalue_describe_type_simple(mlhmmv_xvalue_t * pxvalue)272 char* mlhmmv_xvalue_describe_type_simple(mlhmmv_xvalue_t* pxvalue) {
273 if (pxvalue->is_terminal) {
274 return mt_describe_type_simple(pxvalue->terminal_mlrval.type);
275 } else {
276 return "map";
277 }
278 }
279
mlhmmv_xvalue_copy_keys_indexed(mlhmmv_xvalue_t * pmvalue,sllmv_t * pmvkeys)280 sllv_t* mlhmmv_xvalue_copy_keys_indexed(mlhmmv_xvalue_t* pmvalue, sllmv_t* pmvkeys) { // xxx code dedupe
281 int error;
282 if (pmvkeys == NULL || pmvkeys->length == 0) {
283 return mlhmmv_xvalue_copy_keys_nonindexed(pmvalue);
284 } else if (pmvalue->is_terminal) { // xxx copy this check up to oosvar case too
285 return sllv_alloc();
286 } else {
287 mlhmmv_level_entry_t* pfromentry = mlhmmv_level_look_up_and_ref_entry(pmvalue->pnext_level,
288 pmvkeys->phead, &error);
289 if (pfromentry != NULL) {
290 return mlhmmv_xvalue_copy_keys_nonindexed(&pfromentry->level_xvalue);
291 } else {
292 return sllv_alloc();
293 }
294 }
295 }
296
mlhmmv_xvalue_copy_keys_nonindexed(mlhmmv_xvalue_t * pvalue)297 sllv_t* mlhmmv_xvalue_copy_keys_nonindexed(mlhmmv_xvalue_t* pvalue) {
298 sllv_t* pkeys = sllv_alloc();
299
300 if (!pvalue->is_terminal) {
301 mlhmmv_level_t* pnext_level = pvalue->pnext_level;
302 for (mlhmmv_level_entry_t* pe = pnext_level->phead; pe != NULL; pe = pe->pnext) {
303 mv_t* p = mv_alloc_copy(&pe->level_key);
304 sllv_append(pkeys, p);
305 }
306 }
307
308 return pkeys;
309 }
310
311 // ----------------------------------------------------------------
mlhmmv_xvalues_to_lrecs_lashed(mlhmmv_xvalue_t ** ptop_values,int num_submaps,mv_t * pbasenames,sllmv_t * pnames,sllv_t * poutrecs,int do_full_prefixing,char * flatten_separator)312 void mlhmmv_xvalues_to_lrecs_lashed(mlhmmv_xvalue_t** ptop_values, int num_submaps, mv_t* pbasenames, sllmv_t* pnames,
313 sllv_t* poutrecs, int do_full_prefixing, char* flatten_separator)
314 {
315
316 // First is primary and rest are lashed to it (lookups with same keys as primary).
317 if (ptop_values[0] == NULL) {
318 // No such entry in the mlhmmv results in no output records
319 } else if (ptop_values[0]->is_terminal && mv_is_present(&ptop_values[0]->terminal_mlrval)) {
320 lrec_t* poutrec = lrec_unbacked_alloc();
321 for (int i = 0; i < num_submaps; i++) {
322 // E.g. '@v = 3' at the top level of the mlhmmv.
323 if (ptop_values[i]->is_terminal && mv_is_present(&ptop_values[i]->terminal_mlrval)) {
324 lrec_put(poutrec,
325 mv_alloc_format_val(&pbasenames[i]),
326 mv_alloc_format_val(&ptop_values[i]->terminal_mlrval), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
327 }
328 }
329 sllv_append(poutrecs, poutrec);
330 } else {
331 // E.g. '@v = {...}' at the top level of the map, where the map is an oosvar
332 // submap keyed by oosvar-name 'v', or a localvar value, or a map literal. This
333 // needs to be flattened down to an lrec which is a list of key-value pairs. We
334 // recursively invoke mlhhmv_levels_to_lrecs_lashed_across_records for each of the
335 // name-list entries, one map level deeper each call, then from there invoke
336 // mlhhmv_levels_to_lrecs_lashed_within_records on any remaining map levels.
337 lrec_t* ptemplate = lrec_unbacked_alloc();
338
339 mlhmmv_level_t** ppnext_levels = mlr_malloc_or_die(num_submaps * sizeof(mlhmmv_level_t*));
340 char** oosvar_names = mlr_malloc_or_die(num_submaps * sizeof(char*));
341 for (int i = 0; i < num_submaps; i++) {
342 if (ptop_values[i] == NULL || ptop_values[i]->is_terminal) {
343 ppnext_levels[i] = NULL;
344 oosvar_names[i] = NULL;
345 } else {
346 ppnext_levels[i] = ptop_values[i]->pnext_level;
347 oosvar_names[i] = mv_alloc_format_val(&pbasenames[i]);
348 }
349 }
350
351 mlhhmv_levels_to_lrecs_lashed_across_records(ppnext_levels, oosvar_names, num_submaps,
352 pnames->phead, ptemplate, poutrecs, do_full_prefixing, flatten_separator);
353
354 for (int i = 0; i < num_submaps; i++) {
355 free(oosvar_names[i]);
356 }
357 free(oosvar_names);
358 free(ppnext_levels);
359
360 lrec_free(ptemplate);
361 }
362 }
363
364 // ================================================================
mlhmmv_level_alloc()365 mlhmmv_level_t* mlhmmv_level_alloc() {
366 mlhmmv_level_t* plevel = mlr_malloc_or_die(sizeof(mlhmmv_level_t));
367 mlhmmv_level_init(plevel, MLHMMV_INITIAL_ARRAY_LENGTH);
368 return plevel;
369 }
370
371 // ----------------------------------------------------------------
mlhmmv_level_init(mlhmmv_level_t * plevel,int length)372 static void mlhmmv_level_init(mlhmmv_level_t *plevel, int length) {
373 plevel->num_occupied = 0;
374 plevel->num_freed = 0;
375 plevel->array_length = length;
376
377 plevel->entries = (mlhmmv_level_entry_t*)mlr_malloc_or_die(sizeof(mlhmmv_level_entry_t) * length);
378 // Don't do mlhmmv_level_entry_clear() of all entries at init time, since this has a
379 // drastic effect on the time needed to construct an empty map (and miller
380 // constructs an awful lot of those). The attributes there are don't-cares
381 // if the corresponding entry state is EMPTY. They are set on put, and
382 // mutated on remove.
383
384 plevel->states = (mlhmmv_level_entry_state_t*)mlr_malloc_or_die(sizeof(mlhmmv_level_entry_state_t) * length);
385 memset(plevel->states, EMPTY, length);
386
387 plevel->phead = NULL;
388 plevel->ptail = NULL;
389 }
390
391 // ----------------------------------------------------------------
mlhmmv_level_free(mlhmmv_level_t * plevel)392 void mlhmmv_level_free(mlhmmv_level_t* plevel) {
393 for (mlhmmv_level_entry_t* pentry = plevel->phead; pentry != NULL; pentry = pentry->pnext) {
394 mv_free(&pentry->level_key);
395 if (pentry->level_xvalue.is_terminal) {
396 mv_free(&pentry->level_xvalue.terminal_mlrval);
397 } else {
398 mlhmmv_level_free(pentry->level_xvalue.pnext_level);
399 }
400 }
401 free(plevel->entries);
402 free(plevel->states);
403 plevel->entries = NULL;
404 plevel->num_occupied = 0;
405 plevel->num_freed = 0;
406 plevel->array_length = 0;
407 free(plevel);
408 }
409
410 // ----------------------------------------------------------------
mlhmmv_level_clear(mlhmmv_level_t * plevel)411 void mlhmmv_level_clear(mlhmmv_level_t* plevel) {
412 if (plevel->phead == NULL)
413 return;
414
415 for (mlhmmv_level_entry_t* pentry = plevel->phead; pentry != NULL; pentry = pentry->pnext) {
416 if (pentry->level_xvalue.is_terminal) {
417 mv_free(&pentry->level_xvalue.terminal_mlrval);
418 } else {
419 mlhmmv_level_free(pentry->level_xvalue.pnext_level);
420 }
421 mv_free(&pentry->level_key);
422 }
423 plevel->num_occupied = 0;
424 plevel->num_freed = 0;
425 plevel->phead = NULL;
426 plevel->ptail = NULL;
427
428 memset(plevel->states, EMPTY, plevel->array_length);
429 }
430
431 // ----------------------------------------------------------------
mlhmmv_level_has_key(mlhmmv_level_t * plevel,mv_t * plevel_key)432 int mlhmmv_level_has_key(mlhmmv_level_t* plevel, mv_t* plevel_key) {
433 int ideal_index = 0;
434 int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
435 if (plevel->states[index] == OCCUPIED)
436 return TRUE;
437 else if (plevel->states[index] == EMPTY)
438 return FALSE;
439 else {
440 fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
441 exit(1);
442 }
443 }
444
445 // ----------------------------------------------------------------
446 // Returns >=0 for where the key is *or* should go (end of chain).
447
mlhmmv_level_find_index_for_key(mlhmmv_level_t * plevel,mv_t * plevel_key,int * pideal_index)448 static int mlhmmv_level_find_index_for_key(mlhmmv_level_t* plevel, mv_t* plevel_key, int* pideal_index) {
449 int hash = mlhmmv_hash_func(plevel_key);
450 int index = mlr_canonical_mod(hash, plevel->array_length);
451 *pideal_index = index;
452 int num_tries = 0;
453
454 while (TRUE) {
455 mlhmmv_level_entry_t* pentry = &plevel->entries[index];
456 if (plevel->states[index] == OCCUPIED) {
457 mv_t* ekey = &pentry->level_key;
458 // Existing key found in chain.
459 if (mv_equals_si(plevel_key, ekey))
460 return index;
461 } else if (plevel->states[index] == EMPTY) {
462 return index;
463 }
464
465 // If the current entry has been freed, i.e. previously occupied,
466 // the sought index may be further down the chain. So we must
467 // continue looking.
468 if (++num_tries >= plevel->array_length) {
469 fprintf(stderr,
470 "%s: Coding error: table full even after enlargement.\n", MLR_GLOBALS.bargv0);
471 exit(1);
472 }
473
474 // Linear probing.
475 if (++index >= plevel->array_length)
476 index = 0;
477 }
478 MLR_INTERNAL_CODING_ERROR();
479 return -1; // not reached
480 }
481
482 // ----------------------------------------------------------------
mlhmmv_level_look_up_and_ref_entry(mlhmmv_level_t * plevel,sllmve_t * prestkeys,int * perror)483 static mlhmmv_level_entry_t* mlhmmv_level_look_up_and_ref_entry(mlhmmv_level_t* plevel,
484 sllmve_t* prestkeys, int* perror)
485 {
486 if (perror)
487 *perror = MLHMMV_ERROR_NONE;
488 if (prestkeys == NULL) {
489 if (perror)
490 *perror = MLHMMV_ERROR_KEYLIST_TOO_SHALLOW;
491 return NULL;
492 }
493 mlhmmv_level_entry_t* plevel_entry = mlhmmv_level_get_next_level_entry(plevel, &prestkeys->value, NULL);
494 while (prestkeys->pnext != NULL) {
495 if (plevel_entry == NULL) {
496 return NULL;
497 }
498 if (plevel_entry->level_xvalue.is_terminal) {
499 if (perror)
500 *perror = MLHMMV_ERROR_KEYLIST_TOO_DEEP;
501 return NULL;
502 }
503 plevel = plevel_entry->level_xvalue.pnext_level;
504 prestkeys = prestkeys->pnext;
505 plevel_entry = mlhmmv_level_get_next_level_entry(plevel_entry->level_xvalue.pnext_level,
506 &prestkeys->value, NULL);
507 }
508 return plevel_entry;
509 }
510
511 // ----------------------------------------------------------------
mlhmmv_level_get_next_level_entry(mlhmmv_level_t * plevel,mv_t * plevel_key,int * pindex)512 static mlhmmv_level_entry_t* mlhmmv_level_get_next_level_entry(mlhmmv_level_t* plevel, mv_t* plevel_key, int* pindex) {
513 int ideal_index = 0;
514 int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
515 mlhmmv_level_entry_t* pentry = &plevel->entries[index];
516
517 if (pindex != NULL)
518 *pindex = index;
519
520 if (plevel->states[index] == OCCUPIED)
521 return pentry;
522 else if (plevel->states[index] == EMPTY)
523 return NULL;
524 else {
525 fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
526 exit(1);
527 }
528 }
529
530 // ----------------------------------------------------------------
mlhmmv_level_get_next_level_xvalue(mlhmmv_level_t * plevel,mv_t * plevel_key)531 static mlhmmv_xvalue_t* mlhmmv_level_get_next_level_xvalue(mlhmmv_level_t* plevel, mv_t* plevel_key) {
532 if (plevel == NULL)
533 return NULL;
534 mlhmmv_level_entry_t* pentry = mlhmmv_level_get_next_level_entry(plevel, plevel_key, NULL);
535 if (pentry == NULL)
536 return NULL;
537 else
538 return &pentry->level_xvalue;
539 }
540
541 // ----------------------------------------------------------------
mlhmmv_level_look_up_and_ref_terminal(mlhmmv_level_t * plevel,sllmv_t * pmvkeys,int * perror)542 mv_t* mlhmmv_level_look_up_and_ref_terminal(mlhmmv_level_t* plevel, sllmv_t* pmvkeys, int* perror) {
543 mlhmmv_level_entry_t* plevel_entry = mlhmmv_level_look_up_and_ref_entry(plevel, pmvkeys->phead, perror);
544 if (plevel_entry == NULL) {
545 return NULL;
546 }
547 if (!plevel_entry->level_xvalue.is_terminal) {
548 *perror = MLHMMV_ERROR_KEYLIST_TOO_SHALLOW;
549 return NULL;
550 }
551 return &plevel_entry->level_xvalue.terminal_mlrval;
552 }
553
554 // ----------------------------------------------------------------
mlhmmv_level_look_up_and_ref_xvalue(mlhmmv_level_t * pstart_level,sllmv_t * pmvkeys,int * perror)555 mlhmmv_xvalue_t* mlhmmv_level_look_up_and_ref_xvalue(mlhmmv_level_t* pstart_level, sllmv_t* pmvkeys, int* perror) {
556 *perror = MLHMMV_ERROR_NONE;
557 sllmve_t* prest_keys = pmvkeys->phead;
558 if (prest_keys == NULL) {
559 *perror = MLHMMV_ERROR_KEYLIST_TOO_SHALLOW;
560 return NULL;
561 }
562 mlhmmv_level_t* plevel = pstart_level;
563 if (plevel == NULL) {
564 return NULL;
565 }
566 mlhmmv_level_entry_t* plevel_entry = mlhmmv_level_get_next_level_entry(plevel, &prest_keys->value, NULL);
567 while (prest_keys->pnext != NULL) {
568 if (plevel_entry == NULL) {
569 return NULL;
570 } else {
571 plevel = plevel_entry->level_xvalue.pnext_level;
572 prest_keys = prest_keys->pnext;
573 if (plevel == NULL)
574 return NULL;
575 plevel_entry = mlhmmv_level_get_next_level_entry(plevel, &prest_keys->value, NULL);
576 }
577 }
578 if (plevel_entry == NULL) {
579 *perror = MLHMMV_ERROR_KEYLIST_TOO_DEEP;
580 return NULL;
581 }
582 return &plevel_entry->level_xvalue;
583 }
584
585 // ----------------------------------------------------------------
mlhmmv_level_ref_or_create(mlhmmv_level_t * plevel,sllmve_t * prest_keys)586 static mlhmmv_level_t* mlhmmv_level_ref_or_create(mlhmmv_level_t* plevel, sllmve_t* prest_keys) {
587 if ((plevel->num_occupied + plevel->num_freed) >= (plevel->array_length * LOAD_FACTOR))
588 mlhmmv_level_enlarge(plevel);
589 return mlhmmv_level_get_or_create_no_enlarge(plevel, prest_keys);
590 }
591
592 // ----------------------------------------------------------------
mlhmmv_level_get_or_create_no_enlarge(mlhmmv_level_t * plevel,sllmve_t * prest_keys)593 static mlhmmv_level_t* mlhmmv_level_get_or_create_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys) {
594 mv_t* plevel_key = &prest_keys->value;
595 int ideal_index = 0;
596 int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
597 mlhmmv_level_entry_t* pentry = &plevel->entries[index];
598
599 if (plevel->states[index] == EMPTY) { // End of chain.
600
601 plevel->states[index] = OCCUPIED;
602 plevel->num_occupied++;
603 pentry->ideal_index = ideal_index;
604 pentry->level_key = mv_copy(plevel_key);
605 pentry->level_xvalue.is_terminal = FALSE;
606 pentry->level_xvalue.pnext_level = mlhmmv_level_alloc();
607
608 if (plevel->phead == NULL) { // First entry at this level
609 pentry->pprev = NULL;
610 pentry->pnext = NULL;
611 plevel->phead = pentry;
612 plevel->ptail = pentry;
613 } else { // Subsequent entry at this level
614 pentry->pprev = plevel->ptail;
615 pentry->pnext = NULL;
616 plevel->ptail->pnext = pentry;
617 plevel->ptail = pentry;
618 }
619
620 if (prest_keys->pnext != NULL) {
621 // RECURSE
622 return mlhmmv_level_ref_or_create(pentry->level_xvalue.pnext_level, prest_keys->pnext);
623 } else {
624 return pentry->level_xvalue.pnext_level;
625 }
626
627 } else if (plevel->states[index] == OCCUPIED) { // Existing key found in chain
628
629 if (pentry->level_xvalue.is_terminal) {
630 mv_free(&pentry->level_xvalue.terminal_mlrval);
631 pentry->level_xvalue.is_terminal = FALSE;
632 pentry->level_xvalue.pnext_level = mlhmmv_level_alloc();
633 }
634 if (prest_keys->pnext == NULL) {
635 return pentry->level_xvalue.pnext_level;
636 } else { // RECURSE
637 return mlhmmv_level_ref_or_create(pentry->level_xvalue.pnext_level, prest_keys->pnext);
638 }
639
640 } else {
641 fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
642 exit(1);
643 }
644 }
645
646 // ----------------------------------------------------------------
mlhmmv_level_put_empty_map(mlhmmv_level_t * plevel,mv_t * pkey)647 mlhmmv_level_t* mlhmmv_level_put_empty_map(mlhmmv_level_t* plevel, mv_t* pkey) {
648 int error;
649 mv_t absent = mv_absent();
650
651 sllmve_t e = {
652 .value = *pkey,
653 .free_flags = 0,
654 .pnext = NULL
655 };
656 mlhmmv_level_put_terminal(plevel, &e, &absent); // xxx optimize to avoid 2nd lookup
657 sllmv_t s = {
658 .phead = &e,
659 .ptail = &e,
660 .length = 1
661 };
662 mlhmmv_xvalue_t* pxval = mlhmmv_level_look_up_and_ref_xvalue(plevel, &s, &error);
663 *pxval = mlhmmv_xvalue_alloc_empty_map();
664 return pxval->pnext_level;
665 }
666
667 // ----------------------------------------------------------------
mlhmmv_level_enlarge(mlhmmv_level_t * plevel)668 static void mlhmmv_level_enlarge(mlhmmv_level_t* plevel) {
669 mlhmmv_level_entry_t* old_entries = plevel->entries;
670 mlhmmv_level_entry_state_t* old_states = plevel->states;
671 mlhmmv_level_entry_t* old_head = plevel->phead;
672
673 mlhmmv_level_init(plevel, plevel->array_length*ENLARGEMENT_FACTOR);
674
675 for (mlhmmv_level_entry_t* pentry = old_head; pentry != NULL; pentry = pentry->pnext) {
676 mlhmmv_level_move(plevel, &pentry->level_key, &pentry->level_xvalue);
677 }
678 free(old_entries);
679 free(old_states);
680 }
681
682 // ----------------------------------------------------------------
683 // This is done only on map-level enlargement.
684 // Example:
685 // * level = map["a"], rest_keys = [2, "c"] , terminal_value = 4.
686 // rest_keys = ["e", "f"] , terminal_value = 7.
687 // rest_keys = [6] , terminal_value = "g".
688 //
689 // which is to say for the purposes of this routine
690 //
691 // * level = map["a"], level_key = 2, level_xvalue = non-terminal ["c"] => terminal_value = 4.
692 // level_key = "e", level_xvalue = non-terminal ["f"] => terminal_value = 7.
693 // level_key = 6, level_xvalue = terminal_value = "g".
694
mlhmmv_level_move(mlhmmv_level_t * plevel,mv_t * plevel_key,mlhmmv_xvalue_t * plevel_value)695 static void mlhmmv_level_move(mlhmmv_level_t* plevel, mv_t* plevel_key, mlhmmv_xvalue_t* plevel_value) {
696 int ideal_index = 0;
697 int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
698 mlhmmv_level_entry_t* pentry = &plevel->entries[index];
699
700 if (plevel->states[index] == OCCUPIED) {
701 // Existing key found in chain; put value.
702 pentry->level_xvalue = *plevel_value;
703
704 } else if (plevel->states[index] == EMPTY) {
705 // End of chain.
706 pentry->ideal_index = ideal_index;
707 pentry->level_key = *plevel_key;
708 // For the put API, we copy data passed in. But for internal enlarges, we just need to move pointers around.
709 pentry->level_xvalue = *plevel_value;
710 plevel->states[index] = OCCUPIED;
711
712 if (plevel->phead == NULL) {
713 pentry->pprev = NULL;
714 pentry->pnext = NULL;
715 plevel->phead = pentry;
716 plevel->ptail = pentry;
717 } else {
718 pentry->pprev = plevel->ptail;
719 pentry->pnext = NULL;
720 plevel->ptail->pnext = pentry;
721 plevel->ptail = pentry;
722 }
723 plevel->num_occupied++;
724 }
725 else {
726 fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
727 exit(1);
728 }
729 }
730
731 // ----------------------------------------------------------------
mlhmmv_level_put_xvalue(mlhmmv_level_t * plevel,sllmve_t * prest_keys,mlhmmv_xvalue_t * pvalue)732 void mlhmmv_level_put_xvalue(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mlhmmv_xvalue_t* pvalue) { // xxx 'copy' into name
733 if ((plevel->num_occupied + plevel->num_freed) >= (plevel->array_length * LOAD_FACTOR))
734 mlhmmv_level_enlarge(plevel);
735 mlhmmv_level_put_xvalue_no_enlarge(plevel, prest_keys, pvalue);
736 }
737
mlhmmv_level_put_xvalue_singly_keyed(mlhmmv_level_t * plevel,mv_t * pkey,mlhmmv_xvalue_t * pvalue)738 void mlhmmv_level_put_xvalue_singly_keyed(mlhmmv_level_t* plevel, mv_t* pkey, mlhmmv_xvalue_t* pvalue) {
739 sllmve_t e = { .value = *pkey, .free_flags = 0, .pnext = NULL };
740 mlhmmv_level_put_xvalue(plevel, &e, pvalue);
741 }
742
743 // ----------------------------------------------------------------
mlhmmv_level_put_xvalue_no_enlarge(mlhmmv_level_t * plevel,sllmve_t * prest_keys,mlhmmv_xvalue_t * pvalue)744 static void mlhmmv_level_put_xvalue_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys,
745 mlhmmv_xvalue_t* pvalue)
746 {
747 mv_t* plevel_key = &prest_keys->value;
748 int ideal_index = 0;
749 int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
750 mlhmmv_level_entry_t* pentry = &plevel->entries[index];
751
752 if (plevel->states[index] == EMPTY) { // End of chain.
753 pentry->ideal_index = ideal_index;
754 pentry->level_key = mv_copy(plevel_key); // (xxx weird & needs explaining) key is copied ...
755
756 if (prest_keys->pnext == NULL) {
757 pentry->level_xvalue = *pvalue; // (xxx weird & needs explaining) ... but the value is not copied
758 } else {
759 pentry->level_xvalue.is_terminal = FALSE;
760 pentry->level_xvalue.pnext_level = mlhmmv_level_alloc();
761 }
762 plevel->states[index] = OCCUPIED;
763
764 if (plevel->phead == NULL) { // First entry at this level
765 pentry->pprev = NULL;
766 pentry->pnext = NULL;
767 plevel->phead = pentry;
768 plevel->ptail = pentry;
769 } else { // Subsequent entry at this level
770 pentry->pprev = plevel->ptail;
771 pentry->pnext = NULL;
772 plevel->ptail->pnext = pentry;
773 plevel->ptail = pentry;
774 }
775
776 plevel->num_occupied++;
777 if (prest_keys->pnext != NULL) {
778 // RECURSE
779 mlhmmv_level_put_xvalue(pentry->level_xvalue.pnext_level, prest_keys->pnext, pvalue);
780 }
781
782 } else if (plevel->states[index] == OCCUPIED) { // Existing key found in chain
783 if (prest_keys->pnext == NULL) { // Place the terminal at this level
784 if (pentry->level_xvalue.is_terminal) {
785 mv_free(&pentry->level_xvalue.terminal_mlrval);
786 } else {
787 mlhmmv_level_free(pentry->level_xvalue.pnext_level);
788 }
789 pentry->level_xvalue = *pvalue;
790
791 } else { // The terminal will be placed at a deeper level
792 if (pentry->level_xvalue.is_terminal) {
793 mv_free(&pentry->level_xvalue.terminal_mlrval);
794 pentry->level_xvalue.is_terminal = FALSE;
795 pentry->level_xvalue.pnext_level = mlhmmv_level_alloc();
796 }
797 // RECURSE
798 mlhmmv_level_put_xvalue(pentry->level_xvalue.pnext_level, prest_keys->pnext, pvalue);
799 }
800
801 } else {
802 fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
803 exit(1);
804 }
805 }
806
807 // ----------------------------------------------------------------
808 // Example on recursive calls:
809 // * level = map, rest_keys = ["a", 2, "c"] , terminal value = 4.
810 // * level = map["a"], rest_keys = [2, "c"] , terminal value = 4.
811 // * level = map["a"][2], rest_keys = ["c"] , terminal value = 4.
812
mlhmmv_level_put_terminal(mlhmmv_level_t * plevel,sllmve_t * prest_keys,mv_t * pterminal_value)813 void mlhmmv_level_put_terminal(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mv_t* pterminal_value) {
814 if ((plevel->num_occupied + plevel->num_freed) >= (plevel->array_length * LOAD_FACTOR))
815 mlhmmv_level_enlarge(plevel);
816 mlhmmv_level_put_terminal_no_enlarge(plevel, prest_keys, pterminal_value);
817 }
818
mlhmmv_level_put_terminal_singly_keyed(mlhmmv_level_t * plevel,mv_t * pkey,mv_t * pterminal_value)819 void mlhmmv_level_put_terminal_singly_keyed(mlhmmv_level_t* plevel, mv_t* pkey, mv_t* pterminal_value) {
820 sllmve_t e = { .value = *pkey, .free_flags = 0, .pnext = NULL };
821 mlhmmv_level_put_terminal(plevel, &e, pterminal_value);
822 }
823
824 // ----------------------------------------------------------------
mlhmmv_level_put_terminal_no_enlarge(mlhmmv_level_t * plevel,sllmve_t * prest_keys,mv_t * pterminal_value)825 static void mlhmmv_level_put_terminal_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mv_t* pterminal_value) {
826 mv_t* plevel_key = &prest_keys->value;
827 int ideal_index = 0;
828 int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
829 mlhmmv_level_entry_t* pentry = &plevel->entries[index];
830
831 if (plevel->states[index] == EMPTY) { // End of chain.
832 pentry->ideal_index = ideal_index;
833 pentry->level_key = mv_copy(plevel_key); // <------------------------- copy key
834
835 if (prest_keys->pnext == NULL) {
836 pentry->level_xvalue.is_terminal = TRUE;
837 pentry->level_xvalue.terminal_mlrval = mv_copy(pterminal_value); // <--------------- copy value
838 } else {
839 pentry->level_xvalue.is_terminal = FALSE;
840 pentry->level_xvalue.pnext_level = mlhmmv_level_alloc();
841 }
842 plevel->states[index] = OCCUPIED;
843
844 if (plevel->phead == NULL) { // First entry at this level
845 pentry->pprev = NULL;
846 pentry->pnext = NULL;
847 plevel->phead = pentry;
848 plevel->ptail = pentry;
849 } else { // Subsequent entry at this level
850 pentry->pprev = plevel->ptail;
851 pentry->pnext = NULL;
852 plevel->ptail->pnext = pentry;
853 plevel->ptail = pentry;
854 }
855
856 plevel->num_occupied++;
857 if (prest_keys->pnext != NULL) {
858 // RECURSE
859 mlhmmv_level_put_terminal(pentry->level_xvalue.pnext_level, prest_keys->pnext, pterminal_value);
860 }
861
862 } else if (plevel->states[index] == OCCUPIED) { // Existing key found in chain
863 if (prest_keys->pnext == NULL) { // Place the terminal at this level
864 if (pentry->level_xvalue.is_terminal) {
865 mv_free(&pentry->level_xvalue.terminal_mlrval);
866 } else {
867 mlhmmv_level_free(pentry->level_xvalue.pnext_level);
868 }
869 pentry->level_xvalue.is_terminal = TRUE;
870 pentry->level_xvalue.terminal_mlrval = mv_copy(pterminal_value); // <--------------- copy value
871
872 } else { // The terminal will be placed at a deeper level
873 if (pentry->level_xvalue.is_terminal) {
874 mv_free(&pentry->level_xvalue.terminal_mlrval);
875 pentry->level_xvalue.is_terminal = FALSE;
876 pentry->level_xvalue.pnext_level = mlhmmv_level_alloc();
877 }
878 // RECURSE
879 mlhmmv_level_put_terminal(pentry->level_xvalue.pnext_level, prest_keys->pnext, pterminal_value);
880 }
881
882 } else {
883 fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
884 exit(1);
885 }
886 }
887
888 // ----------------------------------------------------------------
mlhmmv_level_to_lrecs(mlhmmv_level_t * plevel,sllmv_t * pkeys,sllmv_t * pnames,sllv_t * poutrecs,int do_full_prefixing,char * flatten_separator)889 void mlhmmv_level_to_lrecs(mlhmmv_level_t* plevel, sllmv_t* pkeys, sllmv_t* pnames, sllv_t* poutrecs,
890 int do_full_prefixing, char* flatten_separator)
891 {
892 mv_t* pfirstkey = &pkeys->phead->value;
893
894 mlhmmv_level_entry_t* ptop_entry = mlhmmv_level_look_up_and_ref_entry(plevel, pkeys->phead, NULL);
895
896 if (ptop_entry == NULL) {
897 // No such entry in the mlhmmv results in no output records
898 } else if (ptop_entry->level_xvalue.is_terminal) {
899 // E.g. '@v = 3' at the top level of the mlhmmv.
900 lrec_t* poutrec = lrec_unbacked_alloc();
901 lrec_put(poutrec,
902 mv_alloc_format_val(pfirstkey),
903 mv_alloc_format_val(&ptop_entry->level_xvalue.terminal_mlrval), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
904 sllv_append(poutrecs, poutrec);
905 } else {
906 // E.g. '@v = {...}' at the top level of the mlhmmv: the map value keyed by oosvar-name 'v' is itself a hashmap.
907 // This needs to be flattened down to an lrec which is a list of key-value pairs. We recursively invoke
908 // mlhmmv_level_to_lrecs_across_records for each of the name-list entries, one map level deeper each call, then
909 // from there invoke mlhmmv_level_to_lrec_within_record on any remaining map levels.
910 lrec_t* ptemplate = lrec_unbacked_alloc();
911 char* oosvar_name = mv_alloc_format_val(pfirstkey);
912 mlhmmv_level_to_lrecs_across_records(ptop_entry->level_xvalue.pnext_level, oosvar_name, pnames->phead,
913 ptemplate, poutrecs, do_full_prefixing, flatten_separator);
914 free(oosvar_name);
915 lrec_free(ptemplate);
916 }
917 }
918
919 // ----------------------------------------------------------------
mlhmmv_level_to_lrecs_across_records(mlhmmv_level_t * plevel,char * prefix,sllmve_t * prestnames,lrec_t * ptemplate,sllv_t * poutrecs,int do_full_prefixing,char * flatten_separator)920 static void mlhmmv_level_to_lrecs_across_records(
921 mlhmmv_level_t* plevel,
922 char* prefix,
923 sllmve_t* prestnames,
924 lrec_t* ptemplate,
925 sllv_t* poutrecs,
926 int do_full_prefixing,
927 char* flatten_separator)
928 {
929 if (prestnames != NULL) {
930 // If there is a namelist entry, pull it out to its own field on the output lrecs.
931 for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
932 mlhmmv_xvalue_t* plevel_value = &pe->level_xvalue;
933 lrec_t* pnextrec = lrec_copy(ptemplate);
934 lrec_put(pnextrec,
935 mv_alloc_format_val(&prestnames->value),
936 mv_alloc_format_val(&pe->level_key), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
937 if (plevel_value->is_terminal) {
938 lrec_put(pnextrec,
939 mlr_strdup_or_die(prefix),
940 mv_alloc_format_val(&plevel_value->terminal_mlrval), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
941 sllv_append(poutrecs, pnextrec);
942 } else {
943 mlhmmv_level_to_lrecs_across_records(pe->level_xvalue.pnext_level,
944 prefix, prestnames->pnext, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
945 lrec_free(pnextrec);
946 }
947 }
948
949 } else {
950 // If there are no more remaining namelist entries, flatten remaining map levels using the join separator
951 // (default ":") and use them to create lrec values.
952 lrec_t* pnextrec = lrec_copy(ptemplate);
953 int emit = TRUE;
954 for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
955 mlhmmv_xvalue_t* plevel_value = &pe->level_xvalue;
956 if (plevel_value->is_terminal) {
957 char* temp = mv_alloc_format_val(&pe->level_key);
958 char* next_prefix = do_full_prefixing
959 ? mlr_paste_3_strings(prefix, flatten_separator, temp)
960 : mlr_strdup_or_die(temp);
961 free(temp);
962 lrec_put(pnextrec,
963 next_prefix,
964 mv_alloc_format_val(&plevel_value->terminal_mlrval),
965 FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
966 } else if (do_full_prefixing) {
967 char* temp = mv_alloc_format_val(&pe->level_key);
968 char* next_prefix = mlr_paste_3_strings(prefix, flatten_separator, temp);
969 free(temp);
970 mlhmmv_level_to_lrec_within_record(plevel_value->pnext_level, next_prefix, pnextrec,
971 do_full_prefixing, flatten_separator);
972 free(next_prefix);
973 } else {
974 mlhmmv_level_to_lrecs_across_records(pe->level_xvalue.pnext_level,
975 prefix, NULL, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
976 emit = FALSE;
977 }
978 }
979 if (emit)
980 sllv_append(poutrecs, pnextrec);
981 else
982 lrec_free(pnextrec);
983 }
984 }
985
986 // ----------------------------------------------------------------
mlhmmv_level_to_lrec_within_record(mlhmmv_level_t * plevel,char * prefix,lrec_t * poutrec,int do_full_prefixing,char * flatten_separator)987 static void mlhmmv_level_to_lrec_within_record(
988 mlhmmv_level_t* plevel,
989 char* prefix,
990 lrec_t* poutrec,
991 int do_full_prefixing,
992 char* flatten_separator)
993 {
994 for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
995 mlhmmv_xvalue_t* plevel_value = &pe->level_xvalue;
996 char* temp = mv_alloc_format_val(&pe->level_key);
997 char* next_prefix = do_full_prefixing
998 ? mlr_paste_3_strings(prefix, flatten_separator, temp)
999 : mlr_strdup_or_die(temp);
1000 free(temp);
1001 if (plevel_value->is_terminal) {
1002 lrec_put(poutrec,
1003 next_prefix,
1004 mv_alloc_format_val(&plevel_value->terminal_mlrval),
1005 FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
1006 } else {
1007 mlhmmv_level_to_lrec_within_record(plevel_value->pnext_level, next_prefix, poutrec,
1008 do_full_prefixing, flatten_separator);
1009 free(next_prefix);
1010 }
1011 }
1012 }
1013
1014 // ----------------------------------------------------------------
mlhhmv_levels_to_lrecs_lashed_across_records(mlhmmv_level_t ** pplevels,char ** prefixes,int num_levels,sllmve_t * prestnames,lrec_t * ptemplate,sllv_t * poutrecs,int do_full_prefixing,char * flatten_separator)1015 static void mlhhmv_levels_to_lrecs_lashed_across_records(
1016 mlhmmv_level_t** pplevels,
1017 char** prefixes,
1018 int num_levels,
1019 sllmve_t* prestnames,
1020 lrec_t* ptemplate,
1021 sllv_t* poutrecs,
1022 int do_full_prefixing,
1023 char* flatten_separator)
1024 {
1025 if (prestnames != NULL) {
1026 // If there is a namelist entry, pull it out to its own field on the output lrecs.
1027 // First is iterated over and the rest are lashed (lookups with same keys as primary).
1028 if (pplevels[0] != NULL) {
1029 for (mlhmmv_level_entry_t* pe = pplevels[0]->phead; pe != NULL; pe = pe->pnext) {
1030 mlhmmv_xvalue_t* pfirst_level_value = &pe->level_xvalue;
1031 lrec_t* pnextrec = lrec_copy(ptemplate);
1032 lrec_put(pnextrec,
1033 mv_alloc_format_val(&prestnames->value),
1034 mv_alloc_format_val(&pe->level_key), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
1035
1036 if (pfirst_level_value->is_terminal) {
1037 for (int i = 0; i < num_levels; i++) {
1038 mlhmmv_xvalue_t* plevel_value = mlhmmv_level_get_next_level_xvalue(pplevels[i], &pe->level_key);
1039 if (plevel_value != NULL && plevel_value->is_terminal) {
1040 lrec_put(pnextrec,
1041 mlr_strdup_or_die(prefixes[i]),
1042 mv_alloc_format_val(&plevel_value->terminal_mlrval), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
1043 }
1044 }
1045 sllv_append(poutrecs, pnextrec);
1046 } else {
1047 mlhmmv_level_t** ppnext_levels = mlr_malloc_or_die(num_levels * sizeof(mlhmmv_level_t*));
1048 for (int i = 0; i < num_levels; i++) {
1049 mlhmmv_xvalue_t* plevel_value = mlhmmv_level_get_next_level_xvalue(pplevels[i], &pe->level_key);
1050 if (plevel_value == NULL || plevel_value->is_terminal) {
1051 ppnext_levels[i] = NULL;
1052 } else {
1053 ppnext_levels[i] = plevel_value->pnext_level;
1054 }
1055 }
1056
1057 mlhhmv_levels_to_lrecs_lashed_across_records(ppnext_levels, prefixes, num_levels,
1058 prestnames->pnext, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
1059
1060 free(ppnext_levels);
1061 lrec_free(pnextrec);
1062 }
1063 }
1064 }
1065
1066 } else if (pplevels[0] != NULL) {
1067 // If there are no more remaining namelist entries, flatten remaining map levels using the join separator
1068 // (default ":") and use them to create lrec values.
1069 lrec_t* pnextrec = lrec_copy(ptemplate);
1070 int emit = TRUE;
1071 for (mlhmmv_level_entry_t* pe = pplevels[0]->phead; pe != NULL; pe = pe->pnext) {
1072 if (pe->level_xvalue.is_terminal) {
1073 char* temp = mv_alloc_format_val(&pe->level_key);
1074 for (int i = 0; i < num_levels; i++) {
1075 mlhmmv_xvalue_t* plevel_value = mlhmmv_level_get_next_level_xvalue(pplevels[i], &pe->level_key);
1076 if (plevel_value != NULL && plevel_value->is_terminal) {
1077 char* name = do_full_prefixing
1078 ? mlr_paste_3_strings(prefixes[i], flatten_separator, temp)
1079 : mlr_strdup_or_die(temp);
1080 lrec_put(pnextrec,
1081 name,
1082 mv_alloc_format_val(&plevel_value->terminal_mlrval),
1083 FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
1084 }
1085 }
1086 free(temp);
1087 } else if (do_full_prefixing) {
1088 char* temp = mv_alloc_format_val(&pe->level_key);
1089 mlhmmv_level_t** ppnext_levels = mlr_malloc_or_die(num_levels * sizeof(mlhmmv_level_t*));
1090 char** next_prefixes = mlr_malloc_or_die(num_levels * sizeof(char*));
1091 for (int i = 0; i < num_levels; i++) {
1092 mlhmmv_xvalue_t* plevel_value = mlhmmv_level_get_next_level_xvalue(pplevels[i], &pe->level_key);
1093 if (plevel_value != NULL && !plevel_value->is_terminal) {
1094 ppnext_levels[i] = plevel_value->pnext_level;
1095 next_prefixes[i] = mlr_paste_3_strings(prefixes[i], flatten_separator, temp);
1096 } else {
1097 ppnext_levels[i] = NULL;
1098 next_prefixes[i] = NULL;
1099 }
1100 }
1101
1102 mlhhmv_levels_to_lrecs_lashed_within_records(ppnext_levels, next_prefixes, num_levels,
1103 pnextrec, do_full_prefixing, flatten_separator);
1104
1105 for (int i = 0; i < num_levels; i++) {
1106 free(next_prefixes[i]);
1107 }
1108 free(next_prefixes);
1109 free(ppnext_levels);
1110 free(temp);
1111 } else {
1112 mlhmmv_level_t** ppnext_levels = mlr_malloc_or_die(num_levels * sizeof(mlhmmv_level_t*));
1113 for (int i = 0; i < num_levels; i++) {
1114 mlhmmv_xvalue_t* plevel_value = mlhmmv_level_get_next_level_xvalue(pplevels[i], &pe->level_key);
1115 if (plevel_value->is_terminal) {
1116 ppnext_levels[i] = NULL;
1117 } else {
1118 ppnext_levels[i] = plevel_value->pnext_level;
1119 }
1120 }
1121
1122 mlhhmv_levels_to_lrecs_lashed_across_records(ppnext_levels, prefixes, num_levels,
1123 NULL, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
1124
1125 free(ppnext_levels);
1126 emit = FALSE;
1127 }
1128 }
1129 if (emit)
1130 sllv_append(poutrecs, pnextrec);
1131 else
1132 lrec_free(pnextrec);
1133 }
1134 }
1135
1136 // ----------------------------------------------------------------
mlhhmv_levels_to_lrecs_lashed_within_records(mlhmmv_level_t ** pplevels,char ** prefixes,int num_levels,lrec_t * poutrec,int do_full_prefixing,char * flatten_separator)1137 static void mlhhmv_levels_to_lrecs_lashed_within_records(
1138 mlhmmv_level_t** pplevels,
1139 char** prefixes,
1140 int num_levels,
1141 lrec_t* poutrec,
1142 int do_full_prefixing,
1143 char* flatten_separator)
1144 {
1145 for (mlhmmv_level_entry_t* pe = pplevels[0]->phead; pe != NULL; pe = pe->pnext) {
1146 mlhmmv_xvalue_t* pfirst_level_value = &pe->level_xvalue;
1147 char* temp = mv_alloc_format_val(&pe->level_key);
1148 if (pfirst_level_value->is_terminal) {
1149 for (int i = 0; i < num_levels; i++) {
1150 mlhmmv_xvalue_t* plevel_value = mlhmmv_level_get_next_level_xvalue(pplevels[i], &pe->level_key);
1151 if (plevel_value != NULL && plevel_value->is_terminal) {
1152 char* name = do_full_prefixing
1153 ? mlr_paste_3_strings(prefixes[i], flatten_separator, temp)
1154 : mlr_strdup_or_die(temp);
1155 lrec_put(poutrec,
1156 name,
1157 mv_alloc_format_val(&plevel_value->terminal_mlrval),
1158 FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
1159 }
1160 }
1161 } else {
1162 mlhmmv_level_t** ppnext_levels = mlr_malloc_or_die(num_levels * sizeof(mlhmmv_level_t*));
1163 char** next_prefixes = mlr_malloc_or_die(num_levels * sizeof(char*));
1164 for (int i = 0; i < num_levels; i++) {
1165 mlhmmv_xvalue_t* plevel_value = mlhmmv_level_get_next_level_xvalue(pplevels[i], &pe->level_key);
1166 if (plevel_value->is_terminal) {
1167 ppnext_levels[i] = NULL;
1168 next_prefixes[i] = NULL;
1169 } else {
1170 ppnext_levels[i] = plevel_value->pnext_level;
1171 next_prefixes[i] = do_full_prefixing
1172 ? mlr_paste_3_strings(prefixes[i], flatten_separator, temp)
1173 : mlr_strdup_or_die(temp);
1174 }
1175 }
1176
1177 mlhhmv_levels_to_lrecs_lashed_within_records(ppnext_levels, next_prefixes, num_levels,
1178 poutrec, do_full_prefixing, flatten_separator);
1179
1180 for (int i = 0; i < num_levels; i++) {
1181 free(next_prefixes[i]);
1182 }
1183 free(next_prefixes);
1184 free(ppnext_levels);
1185 }
1186 free(temp);
1187 }
1188 }
1189
1190 // ----------------------------------------------------------------
mlhmmv_level_print_stacked(mlhmmv_level_t * plevel,int depth,int do_final_comma,int quote_keys_always,int quote_values_always,char * line_indent,char * line_term,FILE * ostream)1191 void mlhmmv_level_print_stacked(mlhmmv_level_t* plevel, int depth,
1192 int do_final_comma, int quote_keys_always, int quote_values_always,
1193 char* line_indent, char* line_term, FILE* ostream)
1194 {
1195 if (plevel == NULL) {
1196 return;
1197 }
1198 static char* leader = " ";
1199 // Top-level opening brace goes on a line by itself; subsequents on the same line after the level key.
1200 if (depth == 0)
1201 fprintf(ostream, "%s{%s", line_indent, line_term);
1202 for (mlhmmv_level_entry_t* pentry = plevel->phead; pentry != NULL; pentry = pentry->pnext) {
1203 fprintf(ostream, "%s", line_indent);
1204 for (int i = 0; i <= depth; i++)
1205 fprintf(ostream, "%s", leader);
1206 char* level_key_string = mv_alloc_format_val(&pentry->level_key);
1207 if (quote_keys_always || mv_is_string_or_empty(&pentry->level_key)) {
1208 json_print_string_escaped(ostream, level_key_string);
1209 } else {
1210 fputs(level_key_string, ostream);
1211 }
1212 free(level_key_string);
1213 fprintf(ostream, ": ");
1214
1215 if (pentry->level_xvalue.is_terminal) {
1216 mlhmmv_print_terminal(&pentry->level_xvalue.terminal_mlrval, quote_keys_always,
1217 quote_values_always, ostream);
1218
1219 if (pentry->pnext != NULL)
1220 fprintf(ostream, ",%s", line_term);
1221 else
1222 fprintf(ostream, "%s", line_term);
1223 } else {
1224 fprintf(ostream, "%s{%s", line_indent, line_term);
1225 mlhmmv_level_print_stacked(pentry->level_xvalue.pnext_level, depth + 1,
1226 pentry->pnext != NULL, quote_keys_always, quote_values_always,
1227 line_indent, line_term,
1228 ostream);
1229 }
1230 }
1231 for (int i = 0; i < depth; i++)
1232 fprintf(ostream, "%s", leader);
1233 if (do_final_comma)
1234 fprintf(ostream, "%s},%s", line_indent, line_term);
1235 else
1236 fprintf(ostream, "%s}%s", line_indent, line_term);
1237 }
1238
1239 // ----------------------------------------------------------------
mlhmmv_level_print_single_line(mlhmmv_level_t * plevel,int depth,int do_final_comma,int quote_keys_always,int quote_values_always,FILE * ostream)1240 static void mlhmmv_level_print_single_line(mlhmmv_level_t* plevel, int depth,
1241 int do_final_comma, int quote_keys_always, int quote_values_always,
1242 FILE* ostream)
1243 {
1244 // Top-level opening brace goes on a line by itself; subsequents on the same line after the level key.
1245 if (depth == 0)
1246 fprintf(ostream, "{ ");
1247 for (mlhmmv_level_entry_t* pentry = plevel->phead; pentry != NULL; pentry = pentry->pnext) {
1248 char* level_key_string = mv_alloc_format_val(&pentry->level_key);
1249 if (quote_keys_always || mv_is_string_or_empty(&pentry->level_key)) {
1250 json_print_string_escaped(ostream, level_key_string);
1251 } else {
1252 fputs(level_key_string, ostream);
1253 }
1254 free(level_key_string);
1255 fprintf(ostream, ": ");
1256
1257 if (pentry->level_xvalue.is_terminal) {
1258 char* level_value_string = mv_alloc_format_val(&pentry->level_xvalue.terminal_mlrval);
1259
1260 if (quote_values_always) {
1261 json_print_string_escaped(ostream,level_value_string);
1262 } else if (pentry->level_xvalue.terminal_mlrval.type == MT_STRING) {
1263 double parsed;
1264 if (mlr_try_float_from_string(level_value_string, &parsed)) {
1265 json_decimal_print(ostream, level_value_string, parsed);
1266 } else if (streq(level_value_string, "true") || streq(level_value_string, "false")) {
1267 fprintf(ostream, "%s", level_value_string);
1268 } else {
1269 json_print_string_escaped(ostream,level_value_string);
1270 }
1271 } else {
1272 fprintf(ostream, "%s", level_value_string);
1273 }
1274
1275 free(level_value_string);
1276 if (pentry->pnext != NULL)
1277 fprintf(ostream, ", ");
1278 } else {
1279 fprintf(ostream, "{");
1280 mlhmmv_level_print_single_line(pentry->level_xvalue.pnext_level, depth + 1,
1281 pentry->pnext != NULL, quote_keys_always, quote_values_always, ostream);
1282 }
1283 }
1284 if (do_final_comma)
1285 fprintf(ostream, " },");
1286 else
1287 fprintf(ostream, " }");
1288 }
1289
1290 // ----------------------------------------------------------------
mlhmmv_level_remove(mlhmmv_level_t * plevel,sllmve_t * prestkeys)1291 void mlhmmv_level_remove(mlhmmv_level_t* plevel, sllmve_t* prestkeys) {
1292 if (plevel == NULL) // nonesuch
1293 return;
1294 if (prestkeys == NULL) // restkeys too short
1295 return;
1296
1297 int index = -1;
1298 mlhmmv_level_entry_t* pentry = mlhmmv_level_get_next_level_entry(plevel, &prestkeys->value, &index);
1299 if (pentry == NULL)
1300 return;
1301
1302 if (prestkeys->pnext != NULL) {
1303 // Keep recursing until end of restkeys.
1304 if (pentry->level_xvalue.is_terminal) // restkeys too long
1305 return;
1306 mlhmmv_level_remove(pentry->level_xvalue.pnext_level, prestkeys->pnext);
1307
1308 } else {
1309 // 1. Excise the node and its descendants from the storage tree
1310 if (plevel->states[index] != OCCUPIED) {
1311 fprintf(stderr, "%s: mlhmmv_root_remove: did not find end of chain.\n", MLR_GLOBALS.bargv0);
1312 exit(1);
1313 }
1314
1315 mv_free(&pentry->level_key);
1316 pentry->ideal_index = -1;
1317 plevel->states[index] = DELETED;
1318
1319 if (pentry == plevel->phead) {
1320 if (pentry == plevel->ptail) {
1321 plevel->phead = NULL;
1322 plevel->ptail = NULL;
1323 } else {
1324 plevel->phead = pentry->pnext;
1325 pentry->pnext->pprev = NULL;
1326 }
1327 } else if (pentry == plevel->ptail) {
1328 plevel->ptail = pentry->pprev;
1329 pentry->pprev->pnext = NULL;
1330 } else {
1331 pentry->pprev->pnext = pentry->pnext;
1332 pentry->pnext->pprev = pentry->pprev;
1333 }
1334
1335 plevel->num_freed++;
1336 plevel->num_occupied--;
1337
1338 // 2. Free the memory for the node and its descendants
1339 if (pentry->level_xvalue.is_terminal) {
1340 mv_free(&pentry->level_xvalue.terminal_mlrval);
1341 } else {
1342 mlhmmv_level_free(pentry->level_xvalue.pnext_level);
1343 }
1344 }
1345 }
1346
1347 // ================================================================
mlhmmv_root_alloc()1348 mlhmmv_root_t* mlhmmv_root_alloc() {
1349 mlhmmv_root_t* pmap = mlr_malloc_or_die(sizeof(mlhmmv_root_t));
1350 pmap->root_xvalue = mlhmmv_xvalue_alloc_empty_map();
1351 return pmap;
1352 }
1353
1354 // ----------------------------------------------------------------
mlhmmv_root_free(mlhmmv_root_t * pmap)1355 void mlhmmv_root_free(mlhmmv_root_t* pmap) {
1356 if (pmap == NULL)
1357 return;
1358 mlhmmv_level_free(pmap->root_xvalue.pnext_level);
1359 free(pmap);
1360 }
1361
1362 // ----------------------------------------------------------------
mlhmmv_root_clear(mlhmmv_root_t * pmap)1363 void mlhmmv_root_clear(mlhmmv_root_t* pmap) {
1364 mlhmmv_level_clear(pmap->root_xvalue.pnext_level);
1365 }
1366
1367 // ----------------------------------------------------------------
mlhmmv_root_look_up_and_ref_terminal(mlhmmv_root_t * pmap,sllmv_t * pmvkeys,int * perror)1368 mv_t* mlhmmv_root_look_up_and_ref_terminal(mlhmmv_root_t* pmap, sllmv_t* pmvkeys, int* perror) {
1369 mlhmmv_level_entry_t* plevel_entry = mlhmmv_level_look_up_and_ref_entry(pmap->root_xvalue.pnext_level,
1370 pmvkeys->phead, perror);
1371 if (plevel_entry == NULL) {
1372 return NULL;
1373 }
1374 if (!plevel_entry->level_xvalue.is_terminal) {
1375 *perror = MLHMMV_ERROR_KEYLIST_TOO_SHALLOW;
1376 return NULL;
1377 }
1378 return &plevel_entry->level_xvalue.terminal_mlrval;
1379 }
1380
1381 // ----------------------------------------------------------------
1382 // Example on recursive calls:
1383 // * level = map, rest_keys = ["a", 2, "c"]
1384 // * level = map["a"], rest_keys = [2, "c"]
1385 // * level = map["a"][2], rest_keys = ["c"]
mlhmmv_root_look_up_or_create_then_ref_level(mlhmmv_root_t * pmap,sllmv_t * pmvkeys)1386 mlhmmv_level_t* mlhmmv_root_look_up_or_create_then_ref_level(mlhmmv_root_t* pmap, sllmv_t* pmvkeys) {
1387 return mlhmmv_level_ref_or_create(pmap->root_xvalue.pnext_level, pmvkeys->phead);
1388 }
1389
1390 // ----------------------------------------------------------------
1391 // Example: keys = ["a", 2, "c"] and value = 4.
mlhmmv_root_put_terminal(mlhmmv_root_t * pmap,sllmv_t * pmvkeys,mv_t * pterminal_value)1392 void mlhmmv_root_put_terminal(mlhmmv_root_t* pmap, sllmv_t* pmvkeys, mv_t* pterminal_value) {
1393 mlhmmv_level_put_terminal(pmap->root_xvalue.pnext_level, pmvkeys->phead, pterminal_value);
1394 }
1395
1396 // ----------------------------------------------------------------
mlhmmv_root_put_xvalue(mlhmmv_root_t * pmap,sllmv_t * pmvkeys,mlhmmv_xvalue_t * pvalue)1397 static void mlhmmv_root_put_xvalue(mlhmmv_root_t* pmap, sllmv_t* pmvkeys, mlhmmv_xvalue_t* pvalue) {
1398 mlhmmv_level_put_xvalue(pmap->root_xvalue.pnext_level, pmvkeys->phead, pvalue);
1399 }
1400
1401 // ----------------------------------------------------------------
mlhmmv_root_copy_submap(mlhmmv_root_t * pmap,sllmv_t * ptokeys,sllmv_t * pfromkeys)1402 void mlhmmv_root_copy_submap(mlhmmv_root_t* pmap, sllmv_t* ptokeys, sllmv_t* pfromkeys) {
1403 int error = 0;
1404 mlhmmv_level_entry_t* pfromentry = mlhmmv_level_look_up_and_ref_entry(pmap->root_xvalue.pnext_level,
1405 pfromkeys->phead, &error);
1406 if (pfromentry != NULL) {
1407 mlhmmv_xvalue_t submap = mlhmmv_xvalue_copy(&pfromentry->level_xvalue);
1408 mlhmmv_root_put_xvalue(pmap, ptokeys, &submap);
1409 }
1410 }
1411
1412 // ----------------------------------------------------------------
mlhmmv_root_copy_keys_from_submap(mlhmmv_root_t * pmap,sllmv_t * pmvkeys)1413 sllv_t* mlhmmv_root_copy_keys_from_submap(mlhmmv_root_t* pmap, sllmv_t* pmvkeys) {
1414 int error;
1415 if (pmvkeys->length == 0) {
1416 mlhmmv_xvalue_t root_value = (mlhmmv_xvalue_t) {
1417 .is_terminal = FALSE,
1418 .terminal_mlrval = mv_absent(),
1419 .pnext_level = pmap->root_xvalue.pnext_level,
1420 };
1421 return mlhmmv_xvalue_copy_keys_nonindexed(&root_value);
1422 } else {
1423 mlhmmv_level_entry_t* pfromentry = mlhmmv_level_look_up_and_ref_entry(
1424 pmap->root_xvalue.pnext_level, pmvkeys->phead, &error);
1425 if (pfromentry != NULL) {
1426 return mlhmmv_xvalue_copy_keys_nonindexed(&pfromentry->level_xvalue);
1427 } else {
1428 return sllv_alloc();
1429 }
1430 }
1431 }
1432
1433 // ----------------------------------------------------------------
1434 // Removes entries from a specified level downward, unsetting any maps which become empty as a result. For example, if
1435 // e.g. a=>b=>c=>4 and the c level is to be removed, then all up-nodes are emptied out & should be pruned.
1436 // * If restkeys too long (e.g. 'unset $a["b"]["c"]' with data "a":"b":3): do nothing.
1437 // * If restkeys just right: (e.g. 'unset $a["b"]' with data "a":"b":3) remove the terminal mlrval.
1438 // * If restkeys is too short: (e.g. 'unset $a["b"]' with data "a":"b":"c":4): remove the level and all below.
1439
mlhmmv_root_remove(mlhmmv_root_t * pmap,sllmv_t * prestkeys)1440 void mlhmmv_root_remove(mlhmmv_root_t* pmap, sllmv_t* prestkeys) {
1441 if (prestkeys == NULL) {
1442 return;
1443 } else if (prestkeys->phead == NULL) {
1444 mlhmmv_level_free(pmap->root_xvalue.pnext_level);
1445 pmap->root_xvalue.pnext_level = mlhmmv_level_alloc();
1446 return;
1447 } else {
1448 mlhmmv_level_remove(pmap->root_xvalue.pnext_level, prestkeys->phead);
1449 }
1450 }
1451
1452 // ----------------------------------------------------------------
1453 // For 'emit all' and 'emitp all'.
mlhmmv_root_all_to_lrecs(mlhmmv_root_t * pmap,sllmv_t * pnames,sllv_t * poutrecs,int do_full_prefixing,char * flatten_separator)1454 void mlhmmv_root_all_to_lrecs(mlhmmv_root_t* pmap, sllmv_t* pnames, sllv_t* poutrecs, int do_full_prefixing,
1455 char* flatten_separator)
1456 {
1457 for (mlhmmv_level_entry_t* pentry = pmap->root_xvalue.pnext_level->phead; pentry != NULL; pentry = pentry->pnext) {
1458 sllmv_t* pkey = sllmv_single_no_free(&pentry->level_key);
1459 mlhmmv_root_partial_to_lrecs(pmap, pkey, pnames, poutrecs, do_full_prefixing, flatten_separator);
1460 sllmv_free(pkey);
1461 }
1462 }
1463
1464 // ----------------------------------------------------------------
1465 // For 'emit' and 'emitp': the latter has do_full_prefixing == TRUE. These allocate lrecs, appended to the poutrecs
1466 // list.
1467
1468 // * pmap is the base-level oosvar multi-level hashmap.
1469 // * pkeys specify the level in the mlhmmv at which to produce data.
1470 // * pnames is used to pull subsequent-level keys out into separate fields.
1471 // * In case pnames isn't long enough to reach a terminal mlrval level in the mlhmmv,
1472 // do_full_prefixing specifies whether to concatenate nested mlhmmv keys into single lrec keys.
1473 //
1474 // Examples:
1475
1476 // * pkeys reaches a terminal level:
1477 //
1478 // $ mlr --opprint put -q '@sum += $x; end { emit @sum }' ../data/small
1479 // sum
1480 // 4.536294
1481
1482 // * pkeys reaches terminal levels:
1483 //
1484 // $ mlr --opprint put -q '@sum[$a][$b] += $x; end { emit @sum, "a", "b" }' ../data/small
1485 // a b sum
1486 // pan pan 0.346790
1487 // pan wye 0.502626
1488 // eks pan 0.758680
1489 // eks wye 0.381399
1490 // eks zee 0.611784
1491 // wye wye 0.204603
1492 // wye pan 0.573289
1493 // zee pan 0.527126
1494 // zee wye 0.598554
1495 // hat wye 0.031442
1496
1497 // * pkeys reaches non-terminal levels: non-prefixed:
1498 //
1499 // $ mlr --opprint put -q '@sum[$a][$b] += $x; end { emit @sum, "a" }' ../data/small
1500 // a pan wye
1501 // pan 0.346790 0.502626
1502 //
1503 // a pan wye zee
1504 // eks 0.758680 0.381399 0.611784
1505 //
1506 // a wye pan
1507 // wye 0.204603 0.573289
1508 //
1509 // a pan wye
1510 // zee 0.527126 0.598554
1511 //
1512 // a wye
1513 // hat 0.031442
1514
1515 // * pkeys reaches non-terminal levels: prefixed:
1516 //
1517 // $ mlr --opprint put -q '@sum[$a][$b] += $x; end { emitp @sum, "a" }' ../data/small
1518 // a sum:pan sum:wye
1519 // pan 0.346790 0.502626
1520 //
1521 // a sum:pan sum:wye sum:zee
1522 // eks 0.758680 0.381399 0.611784
1523 //
1524 // a sum:wye sum:pan
1525 // wye 0.204603 0.573289
1526 //
1527 // a sum:pan sum:wye
1528 // zee 0.527126 0.598554
1529 //
1530 // a sum:wye
1531 // hat 0.031442
1532
mlhmmv_root_partial_to_lrecs(mlhmmv_root_t * pmap,sllmv_t * pkeys,sllmv_t * pnames,sllv_t * poutrecs,int do_full_prefixing,char * flatten_separator)1533 void mlhmmv_root_partial_to_lrecs(mlhmmv_root_t* pmap, sllmv_t* pkeys, sllmv_t* pnames, sllv_t* poutrecs,
1534 int do_full_prefixing, char* flatten_separator)
1535 {
1536 // There should be at least the oosvar basename, e.g. '@a[b][c]' or '@a[b]' or '@a' but not '@'.
1537 MLR_INTERNAL_CODING_ERROR_IF(pkeys == NULL);
1538 MLR_INTERNAL_CODING_ERROR_IF(pkeys->phead == NULL);
1539
1540 mlhmmv_level_to_lrecs(pmap->root_xvalue.pnext_level, pkeys, pnames, poutrecs, do_full_prefixing,
1541 flatten_separator);
1542 }
1543
1544 // ----------------------------------------------------------------
1545 // This is simply JSON. Example output:
1546 // {
1547 // "0": {
1548 // "fghij": {
1549 // "0": 17
1550 // }
1551 // },
1552 // "3": 4,
1553 // "abcde": {
1554 // "-6": 7
1555 // }
1556 // }
1557
mlhmmv_root_print_json_stacked(mlhmmv_root_t * pmap,int quote_keys_always,int quote_values_always,char * line_indent,char * line_term,FILE * ostream)1558 void mlhmmv_root_print_json_stacked(mlhmmv_root_t* pmap, int quote_keys_always, int quote_values_always,
1559 char* line_indent, char* line_term, FILE* ostream)
1560 {
1561 mlhmmv_level_print_stacked(pmap->root_xvalue.pnext_level, 0, FALSE, quote_keys_always,
1562 quote_values_always, line_indent, line_term, ostream);
1563 }
1564
1565 // ----------------------------------------------------------------
mlhmmv_root_print_json_single_lines(mlhmmv_root_t * pmap,int quote_keys_always,int quote_values_always,char * line_term,FILE * ostream)1566 void mlhmmv_root_print_json_single_lines(mlhmmv_root_t* pmap, int quote_keys_always, int quote_values_always,
1567 char* line_term, FILE* ostream)
1568 {
1569 mlhmmv_level_print_single_line(pmap->root_xvalue.pnext_level, 0, FALSE, quote_keys_always,
1570 quote_values_always, ostream);
1571 fprintf(ostream, "%s", line_term);
1572 }
1573
1574 // Used for emit of localvars. Puts the xvalue in a single-key-value-pair map
1575 // keyed by the specified name. The xvalue is referenced, not copied.
mlhmmv_wrap_name_and_xvalue(mv_t * pname,mlhmmv_xvalue_t * pxval)1576 mlhmmv_root_t* mlhmmv_wrap_name_and_xvalue(mv_t* pname, mlhmmv_xvalue_t* pxval) {
1577 mlhmmv_root_t* pmap = mlhmmv_root_alloc();
1578 mlhmmv_level_put_xvalue_singly_keyed(pmap->root_xvalue.pnext_level, pname, pxval);
1579 return pmap;
1580 }
1581
1582 // Used for takedown of the temporary map returned by mlhmmv_wrap_name_and_xvalue. Since the xvalue there
1583 // is referenced, not copied, mlhmmv_xvalue_free would prematurely free the xvalue. This method releases
1584 // the xvalue so that the remaining, map-internal structures can be freed correctly.
mlhmmv_unwrap_name_and_xvalue(mlhmmv_root_t * pmap)1585 void mlhmmv_unwrap_name_and_xvalue(mlhmmv_root_t* pmap)
1586 {
1587 mlhmmv_level_t* plevel = pmap->root_xvalue.pnext_level;
1588 mlhmmv_level_entry_t* pentry = plevel->phead;
1589 mv_free(&pentry->level_key);
1590 plevel->phead = NULL;
1591 plevel->ptail = NULL;
1592 plevel->num_occupied = 0;
1593 mlhmmv_root_free(pmap);
1594 }
1595