mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-21 18:40:13 +00:00
mlhmmv reorg
This commit is contained in:
parent
0c430b4685
commit
0eb02b1fc0
2 changed files with 334 additions and 331 deletions
|
|
@ -35,14 +35,16 @@
|
|||
#define EMPTY 0xce
|
||||
|
||||
// ================================================================
|
||||
static int mlhmmv_hash_func(mv_t* plevel_key);
|
||||
static void json_decimal_print (FILE* ostream, char* s);
|
||||
static void json_print_string_escaped(FILE* ostream, char* s);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_level_init(mlhmmv_level_t *plevel, int length);
|
||||
static void mlhmmv_level_free(mlhmmv_level_t* plevel);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static int mlhmmv_level_find_index_for_key(mlhmmv_level_t* plevel, mv_t* plevel_key, int* pideal_index);
|
||||
|
||||
static mlhmmv_level_entry_t* mlhmmv_level_look_up_and_ref_entry(
|
||||
mlhmmv_level_t* plevel, sllmve_t* prestkeys, int* perror);
|
||||
|
||||
|
|
@ -50,6 +52,7 @@ static mlhmmv_level_entry_t* mlhmmv_level_get_next_level_entry(mlhmmv_level_t* p
|
|||
static mlhmmv_xvalue_t* mlhmmv_level_get_next_level_xvalue(mlhmmv_level_t* plevel, mv_t* plevel_key);
|
||||
|
||||
static void mlhmmv_level_enlarge(mlhmmv_level_t* plevel);
|
||||
static void mlhmmv_level_move(mlhmmv_level_t* plevel, mv_t* plevel_key, mlhmmv_xvalue_t* plevel_value);
|
||||
|
||||
static void mlhmmv_level_put_xvalue_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mlhmmv_xvalue_t* pvalue);
|
||||
|
||||
|
|
@ -99,6 +102,35 @@ static void mlhmmv_level_remove(mlhmmv_level_t* plevel, sllmve_t* prestkeys, int
|
|||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_root_put_xvalue(mlhmmv_root_t* pmap, sllmv_t* pmvkeys, mlhmmv_xvalue_t* pvalue);
|
||||
|
||||
// ================================================================
|
||||
typedef int mlhmmv_typed_hash_func(mv_t* pa);
|
||||
static int mlhmmv_string_hash_func(mv_t* pa) {
|
||||
return mlr_string_hash_func(pa->u.strv);
|
||||
}
|
||||
static int mlhmmv_int_hash_func(mv_t* pa) {
|
||||
return pa->u.intv;
|
||||
}
|
||||
static int mlhmmv_other_hash_func(mv_t* pa) {
|
||||
fprintf(stderr, "%s: map keys must be of type %s or %s; got %s.\n",
|
||||
MLR_GLOBALS.bargv0,
|
||||
mt_describe_type(MT_STRING),
|
||||
mt_describe_type(MT_INT),
|
||||
mt_describe_type(pa->type));
|
||||
exit(1);
|
||||
}
|
||||
static mlhmmv_typed_hash_func* mlhmmv_hash_func_dispositions[MT_DIM] = {
|
||||
/*ERROR*/ mlhmmv_other_hash_func,
|
||||
/*ABSENT*/ mlhmmv_other_hash_func,
|
||||
/*EMPTY*/ mlhmmv_other_hash_func,
|
||||
/*STRING*/ mlhmmv_string_hash_func,
|
||||
/*INT*/ mlhmmv_int_hash_func,
|
||||
/*FLOAT*/ mlhmmv_other_hash_func,
|
||||
/*BOOL*/ mlhmmv_other_hash_func,
|
||||
};
|
||||
static int mlhmmv_hash_func(mv_t* pa) {
|
||||
return mlhmmv_hash_func_dispositions[pa->type](pa);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
void mlhmmv_print_terminal(mv_t* pmv, int quote_values_always, FILE* ostream) {
|
||||
char* level_value_string = mv_alloc_format_val(pmv);
|
||||
|
|
@ -201,7 +233,8 @@ sllv_t* mlhmmv_xvalue_copy_keys_indexed(mlhmmv_xvalue_t* pmvalue, sllmv_t* pmvke
|
|||
} else if (pmvalue->is_terminal) { // xxx copy this check up to oosvar case too
|
||||
return sllv_alloc();
|
||||
} else {
|
||||
mlhmmv_level_entry_t* pfromentry = mlhmmv_level_look_up_and_ref_entry(pmvalue->pnext_level, pmvkeys->phead, &error);
|
||||
mlhmmv_level_entry_t* pfromentry = mlhmmv_level_look_up_and_ref_entry(pmvalue->pnext_level,
|
||||
pmvkeys->phead, &error);
|
||||
if (pfromentry != NULL) {
|
||||
return mlhmmv_xvalue_copy_keys_nonindexed(&pfromentry->level_value);
|
||||
} else {
|
||||
|
|
@ -287,6 +320,45 @@ mlhmmv_level_t* mlhmmv_level_alloc() {
|
|||
return plevel;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_level_init(mlhmmv_level_t *plevel, int length) {
|
||||
plevel->num_occupied = 0;
|
||||
plevel->num_freed = 0;
|
||||
plevel->array_length = length;
|
||||
|
||||
plevel->entries = (mlhmmv_level_entry_t*)mlr_malloc_or_die(sizeof(mlhmmv_level_entry_t) * length);
|
||||
// Don't do mlhmmv_level_entry_clear() of all entries at init time, since this has a
|
||||
// drastic effect on the time needed to construct an empty map (and miller
|
||||
// constructs an awful lot of those). The attributes there are don't-cares
|
||||
// if the corresponding entry state is EMPTY. They are set on put, and
|
||||
// mutated on remove.
|
||||
|
||||
plevel->states = (mlhmmv_level_entry_state_t*)mlr_malloc_or_die(sizeof(mlhmmv_level_entry_state_t) * length);
|
||||
memset(plevel->states, EMPTY, length);
|
||||
|
||||
plevel->phead = NULL;
|
||||
plevel->ptail = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
void mlhmmv_level_free(mlhmmv_level_t* plevel) {
|
||||
for (mlhmmv_level_entry_t* pentry = plevel->phead; pentry != NULL; pentry = pentry->pnext) {
|
||||
if (pentry->level_value.is_terminal) {
|
||||
mv_free(&pentry->level_value.terminal_mlrval);
|
||||
} else {
|
||||
mlhmmv_level_free(pentry->level_value.pnext_level);
|
||||
}
|
||||
mv_free(&pentry->level_key);
|
||||
}
|
||||
free(plevel->entries);
|
||||
free(plevel->states);
|
||||
plevel->entries = NULL;
|
||||
plevel->num_occupied = 0;
|
||||
plevel->num_freed = 0;
|
||||
plevel->array_length = 0;
|
||||
free(plevel);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
void mlhmmv_level_clear(mlhmmv_level_t* plevel) {
|
||||
if (plevel->phead == NULL)
|
||||
|
|
@ -308,6 +380,34 @@ void mlhmmv_level_clear(mlhmmv_level_t* plevel) {
|
|||
memset(plevel->states, EMPTY, plevel->array_length);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static mlhmmv_level_entry_t* mlhmmv_level_look_up_and_ref_entry(mlhmmv_level_t* plevel,
|
||||
sllmve_t* prestkeys, int* perror)
|
||||
{
|
||||
if (perror)
|
||||
*perror = MLHMMV_ERROR_NONE;
|
||||
if (prestkeys == NULL) {
|
||||
if (perror)
|
||||
*perror = MLHMMV_ERROR_KEYLIST_TOO_SHALLOW;
|
||||
return NULL;
|
||||
}
|
||||
mlhmmv_level_entry_t* plevel_entry = mlhmmv_level_get_next_level_entry(plevel, &prestkeys->value, NULL);
|
||||
while (prestkeys->pnext != NULL) {
|
||||
if (plevel_entry == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (plevel_entry->level_value.is_terminal) {
|
||||
if (perror)
|
||||
*perror = MLHMMV_ERROR_KEYLIST_TOO_DEEP;
|
||||
return NULL;
|
||||
}
|
||||
plevel = plevel_entry->level_value.pnext_level;
|
||||
prestkeys = prestkeys->pnext;
|
||||
plevel_entry = mlhmmv_level_get_next_level_entry(plevel_entry->level_value.pnext_level, &prestkeys->value, NULL);
|
||||
}
|
||||
return plevel_entry;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
mv_t* mlhmmv_level_look_up_and_ref_terminal(mlhmmv_level_t* plevel, sllmv_t* pmvkeys, int* perror) {
|
||||
mlhmmv_level_entry_t* plevel_entry = mlhmmv_level_look_up_and_ref_entry(plevel, pmvkeys->phead, perror);
|
||||
|
|
@ -363,6 +463,70 @@ mlhmmv_level_t* mlhmmv_level_put_empty_map(mlhmmv_level_t* plevel, sllmve_t* pre
|
|||
return pxval->pnext_level;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_level_enlarge(mlhmmv_level_t* plevel) {
|
||||
mlhmmv_level_entry_t* old_entries = plevel->entries;
|
||||
mlhmmv_level_entry_state_t* old_states = plevel->states;
|
||||
mlhmmv_level_entry_t* old_head = plevel->phead;
|
||||
|
||||
mlhmmv_level_init(plevel, plevel->array_length*ENLARGEMENT_FACTOR);
|
||||
|
||||
for (mlhmmv_level_entry_t* pentry = old_head; pentry != NULL; pentry = pentry->pnext) {
|
||||
mlhmmv_level_move(plevel, &pentry->level_key, &pentry->level_value);
|
||||
}
|
||||
free(old_entries);
|
||||
free(old_states);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// This is done only on map-level enlargement.
|
||||
// Example:
|
||||
// * level = map["a"], rest_keys = [2, "c"] , terminal_value = 4.
|
||||
// rest_keys = ["e", "f"] , terminal_value = 7.
|
||||
// rest_keys = [6] , terminal_value = "g".
|
||||
//
|
||||
// which is to say for the purposes of this routine
|
||||
//
|
||||
// * level = map["a"], level_key = 2, level_value = non-terminal ["c"] => terminal_value = 4.
|
||||
// level_key = "e", level_value = non-terminal ["f"] => terminal_value = 7.
|
||||
// level_key = 6, level_value = terminal_value = "g".
|
||||
|
||||
static void mlhmmv_level_move(mlhmmv_level_t* plevel, mv_t* plevel_key, mlhmmv_xvalue_t* plevel_value) {
|
||||
int ideal_index = 0;
|
||||
int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
|
||||
mlhmmv_level_entry_t* pentry = &plevel->entries[index];
|
||||
|
||||
if (plevel->states[index] == OCCUPIED) {
|
||||
// Existing key found in chain; put value.
|
||||
pentry->level_value = *plevel_value;
|
||||
|
||||
} else if (plevel->states[index] == EMPTY) {
|
||||
// End of chain.
|
||||
pentry->ideal_index = ideal_index;
|
||||
pentry->level_key = *plevel_key;
|
||||
// For the put API, we copy data passed in. But for internal enlarges, we just need to move pointers around.
|
||||
pentry->level_value = *plevel_value;
|
||||
plevel->states[index] = OCCUPIED;
|
||||
|
||||
if (plevel->phead == NULL) {
|
||||
pentry->pprev = NULL;
|
||||
pentry->pnext = NULL;
|
||||
plevel->phead = pentry;
|
||||
plevel->ptail = pentry;
|
||||
} else {
|
||||
pentry->pprev = plevel->ptail;
|
||||
pentry->pnext = NULL;
|
||||
plevel->ptail->pnext = pentry;
|
||||
plevel->ptail = pentry;
|
||||
}
|
||||
plevel->num_occupied++;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
void mlhmmv_level_put_xvalue(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mlhmmv_xvalue_t* pvalue) {
|
||||
if ((plevel->num_occupied + plevel->num_freed) >= (plevel->array_length * LOAD_FACTOR))
|
||||
|
|
@ -370,6 +534,70 @@ void mlhmmv_level_put_xvalue(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mlhmm
|
|||
mlhmmv_level_put_xvalue_no_enlarge(plevel, prest_keys, pvalue);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_level_put_xvalue_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys,
|
||||
mlhmmv_xvalue_t* pvalue)
|
||||
{
|
||||
mv_t* plevel_key = &prest_keys->value;
|
||||
int ideal_index = 0;
|
||||
int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
|
||||
mlhmmv_level_entry_t* pentry = &plevel->entries[index];
|
||||
|
||||
if (plevel->states[index] == EMPTY) { // End of chain.
|
||||
pentry->ideal_index = ideal_index;
|
||||
pentry->level_key = mv_copy(plevel_key);
|
||||
|
||||
if (prest_keys->pnext == NULL) {
|
||||
pentry->level_value = *pvalue;
|
||||
} else {
|
||||
pentry->level_value.is_terminal = FALSE;
|
||||
pentry->level_value.pnext_level = mlhmmv_level_alloc();
|
||||
}
|
||||
plevel->states[index] = OCCUPIED;
|
||||
|
||||
if (plevel->phead == NULL) { // First entry at this level
|
||||
pentry->pprev = NULL;
|
||||
pentry->pnext = NULL;
|
||||
plevel->phead = pentry;
|
||||
plevel->ptail = pentry;
|
||||
} else { // Subsequent entry at this level
|
||||
pentry->pprev = plevel->ptail;
|
||||
pentry->pnext = NULL;
|
||||
plevel->ptail->pnext = pentry;
|
||||
plevel->ptail = pentry;
|
||||
}
|
||||
|
||||
plevel->num_occupied++;
|
||||
if (prest_keys->pnext != NULL) {
|
||||
// RECURSE
|
||||
mlhmmv_level_put_xvalue(pentry->level_value.pnext_level, prest_keys->pnext, pvalue);
|
||||
}
|
||||
|
||||
} else if (plevel->states[index] == OCCUPIED) { // Existing key found in chain
|
||||
if (prest_keys->pnext == NULL) { // Place the terminal at this level
|
||||
if (pentry->level_value.is_terminal) {
|
||||
mv_free(&pentry->level_value.terminal_mlrval);
|
||||
} else {
|
||||
mlhmmv_level_free(pentry->level_value.pnext_level);
|
||||
}
|
||||
pentry->level_value = *pvalue;
|
||||
|
||||
} else { // The terminal will be placed at a deeper level
|
||||
if (pentry->level_value.is_terminal) {
|
||||
mv_free(&pentry->level_value.terminal_mlrval);
|
||||
pentry->level_value.is_terminal = FALSE;
|
||||
pentry->level_value.pnext_level = mlhmmv_level_alloc();
|
||||
}
|
||||
// RECURSE
|
||||
mlhmmv_level_put_xvalue(pentry->level_value.pnext_level, prest_keys->pnext, pvalue);
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Example on recursive calls:
|
||||
// * level = map, rest_keys = ["a", 2, "c"] , terminal value = 4.
|
||||
|
|
@ -423,6 +651,101 @@ void mlhmmv_level_to_lrecs(mlhmmv_level_t* plevel, sllmv_t* pkeys, sllmv_t* pnam
|
|||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void 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)
|
||||
{
|
||||
if (prestnames != NULL) {
|
||||
// If there is a namelist entry, pull it out to its own field on the output lrecs.
|
||||
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
|
||||
mlhmmv_xvalue_t* plevel_value = &pe->level_value;
|
||||
lrec_t* pnextrec = lrec_copy(ptemplate);
|
||||
lrec_put(pnextrec,
|
||||
mv_alloc_format_val(&prestnames->value),
|
||||
mv_alloc_format_val(&pe->level_key), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
if (plevel_value->is_terminal) {
|
||||
lrec_put(pnextrec,
|
||||
mlr_strdup_or_die(prefix),
|
||||
mv_alloc_format_val(&plevel_value->terminal_mlrval), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
sllv_append(poutrecs, pnextrec);
|
||||
} else {
|
||||
mlhmmv_level_to_lrecs_across_records(pe->level_value.pnext_level,
|
||||
prefix, prestnames->pnext, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
|
||||
lrec_free(pnextrec);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// If there are no more remaining namelist entries, flatten remaining map levels using the join separator
|
||||
// (default ":") and use them to create lrec values.
|
||||
lrec_t* pnextrec = lrec_copy(ptemplate);
|
||||
int emit = TRUE;
|
||||
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
|
||||
mlhmmv_xvalue_t* plevel_value = &pe->level_value;
|
||||
if (plevel_value->is_terminal) {
|
||||
char* temp = mv_alloc_format_val(&pe->level_key);
|
||||
char* next_prefix = do_full_prefixing
|
||||
? mlr_paste_3_strings(prefix, flatten_separator, temp)
|
||||
: mlr_strdup_or_die(temp);
|
||||
free(temp);
|
||||
lrec_put(pnextrec,
|
||||
next_prefix,
|
||||
mv_alloc_format_val(&plevel_value->terminal_mlrval),
|
||||
FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
} else if (do_full_prefixing) {
|
||||
char* temp = mv_alloc_format_val(&pe->level_key);
|
||||
char* next_prefix = mlr_paste_3_strings(prefix, flatten_separator, temp);
|
||||
free(temp);
|
||||
mlhmmv_level_to_lrec_within_record(plevel_value->pnext_level, next_prefix, pnextrec,
|
||||
do_full_prefixing, flatten_separator);
|
||||
free(next_prefix);
|
||||
} else {
|
||||
mlhmmv_level_to_lrecs_across_records(pe->level_value.pnext_level,
|
||||
prefix, NULL, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
|
||||
emit = FALSE;
|
||||
}
|
||||
}
|
||||
if (emit)
|
||||
sllv_append(poutrecs, pnextrec);
|
||||
else
|
||||
lrec_free(pnextrec);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_level_to_lrec_within_record(
|
||||
mlhmmv_level_t* plevel,
|
||||
char* prefix,
|
||||
lrec_t* poutrec,
|
||||
int do_full_prefixing,
|
||||
char* flatten_separator)
|
||||
{
|
||||
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
|
||||
mlhmmv_xvalue_t* plevel_value = &pe->level_value;
|
||||
char* temp = mv_alloc_format_val(&pe->level_key);
|
||||
char* next_prefix = do_full_prefixing
|
||||
? mlr_paste_3_strings(prefix, flatten_separator, temp)
|
||||
: mlr_strdup_or_die(temp);
|
||||
free(temp);
|
||||
if (plevel_value->is_terminal) {
|
||||
lrec_put(poutrec,
|
||||
next_prefix,
|
||||
mv_alloc_format_val(&plevel_value->terminal_mlrval),
|
||||
FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
} else {
|
||||
mlhmmv_level_to_lrec_within_record(plevel_value->pnext_level, next_prefix, poutrec,
|
||||
do_full_prefixing, flatten_separator);
|
||||
free(next_prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhhmv_levels_to_lrecs_lashed_across_records(
|
||||
mlhmmv_level_t** pplevels,
|
||||
|
|
@ -791,25 +1114,6 @@ mlhmmv_root_t* mlhmmv_root_alloc() {
|
|||
return pmap;
|
||||
}
|
||||
|
||||
static void mlhmmv_level_init(mlhmmv_level_t *plevel, int length) {
|
||||
plevel->num_occupied = 0;
|
||||
plevel->num_freed = 0;
|
||||
plevel->array_length = length;
|
||||
|
||||
plevel->entries = (mlhmmv_level_entry_t*)mlr_malloc_or_die(sizeof(mlhmmv_level_entry_t) * length);
|
||||
// Don't do mlhmmv_level_entry_clear() of all entries at init time, since this has a
|
||||
// drastic effect on the time needed to construct an empty map (and miller
|
||||
// constructs an awful lot of those). The attributes there are don't-cares
|
||||
// if the corresponding entry state is EMPTY. They are set on put, and
|
||||
// mutated on remove.
|
||||
|
||||
plevel->states = (mlhmmv_level_entry_state_t*)mlr_malloc_or_die(sizeof(mlhmmv_level_entry_state_t) * length);
|
||||
memset(plevel->states, EMPTY, length);
|
||||
|
||||
plevel->phead = NULL;
|
||||
plevel->ptail = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
void mlhmmv_root_free(mlhmmv_root_t* pmap) {
|
||||
if (pmap == NULL)
|
||||
|
|
@ -1070,37 +1374,8 @@ void mlhmmv_root_print_json_single_lines(mlhmmv_root_t* pmap, int quote_values_a
|
|||
|
||||
// xxx reorganize to reflect the ordering in the header file. and, maybe split up into separate source files.
|
||||
|
||||
static int mlhmmv_level_find_index_for_key(mlhmmv_level_t* plevel, mv_t* plevel_key, int* pideal_index);
|
||||
|
||||
static void mlhmmv_level_move(mlhmmv_level_t* plevel, mv_t* plevel_key, mlhmmv_xvalue_t* plevel_value);
|
||||
|
||||
static mlhmmv_level_t* mlhmmv_get_or_create_level_aux_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys);
|
||||
|
||||
|
||||
|
||||
static void json_decimal_print(FILE* ostream, char* s);
|
||||
|
||||
static int mlhmmv_hash_func(mv_t* plevel_key);
|
||||
|
||||
static void mlhmmv_level_free(mlhmmv_level_t* plevel) {
|
||||
for (mlhmmv_level_entry_t* pentry = plevel->phead; pentry != NULL; pentry = pentry->pnext) {
|
||||
if (pentry->level_value.is_terminal) {
|
||||
mv_free(&pentry->level_value.terminal_mlrval);
|
||||
} else {
|
||||
mlhmmv_level_free(pentry->level_value.pnext_level);
|
||||
}
|
||||
mv_free(&pentry->level_key);
|
||||
}
|
||||
free(plevel->entries);
|
||||
free(plevel->states);
|
||||
plevel->entries = NULL;
|
||||
plevel->num_occupied = 0;
|
||||
plevel->num_freed = 0;
|
||||
plevel->array_length = 0;
|
||||
|
||||
free(plevel);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Used by get() and remove().
|
||||
// Returns >=0 for where the key is *or* should go (end of chain).
|
||||
|
|
@ -1138,6 +1413,7 @@ static int mlhmmv_level_find_index_for_key(mlhmmv_level_t* plevel, mv_t* plevel_
|
|||
return -1; // not reached
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_level_put_terminal_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys, mv_t* pterminal_value) {
|
||||
mv_t* plevel_key = &prest_keys->value;
|
||||
int ideal_index = 0;
|
||||
|
|
@ -1202,86 +1478,13 @@ static void mlhmmv_level_put_terminal_no_enlarge(mlhmmv_level_t* plevel, sllmve_
|
|||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// This is done only on map-level enlargement.
|
||||
// Example:
|
||||
// * level = map["a"], rest_keys = [2, "c"] , terminal_value = 4.
|
||||
// rest_keys = ["e", "f"] , terminal_value = 7.
|
||||
// rest_keys = [6] , terminal_value = "g".
|
||||
//
|
||||
// which is to say for the purposes of this routine
|
||||
//
|
||||
// * level = map["a"], level_key = 2, level_value = non-terminal ["c"] => terminal_value = 4.
|
||||
// level_key = "e", level_value = non-terminal ["f"] => terminal_value = 7.
|
||||
// level_key = 6, level_value = terminal_value = "g".
|
||||
|
||||
static void mlhmmv_level_move(mlhmmv_level_t* plevel, mv_t* plevel_key, mlhmmv_xvalue_t* plevel_value) {
|
||||
int ideal_index = 0;
|
||||
int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
|
||||
mlhmmv_level_entry_t* pentry = &plevel->entries[index];
|
||||
|
||||
if (plevel->states[index] == OCCUPIED) {
|
||||
// Existing key found in chain; put value.
|
||||
pentry->level_value = *plevel_value;
|
||||
|
||||
} else if (plevel->states[index] == EMPTY) {
|
||||
// End of chain.
|
||||
pentry->ideal_index = ideal_index;
|
||||
pentry->level_key = *plevel_key;
|
||||
// For the put API, we copy data passed in. But for internal enlarges, we just need to move pointers around.
|
||||
pentry->level_value = *plevel_value;
|
||||
plevel->states[index] = OCCUPIED;
|
||||
|
||||
if (plevel->phead == NULL) {
|
||||
pentry->pprev = NULL;
|
||||
pentry->pnext = NULL;
|
||||
plevel->phead = pentry;
|
||||
plevel->ptail = pentry;
|
||||
} else {
|
||||
pentry->pprev = plevel->ptail;
|
||||
pentry->pnext = NULL;
|
||||
plevel->ptail->pnext = pentry;
|
||||
plevel->ptail = pentry;
|
||||
}
|
||||
plevel->num_occupied++;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static mlhmmv_level_entry_t* mlhmmv_level_look_up_and_ref_entry(mlhmmv_level_t* plevel, sllmve_t* prestkeys, int* perror) {
|
||||
if (perror)
|
||||
*perror = MLHMMV_ERROR_NONE;
|
||||
if (prestkeys == NULL) {
|
||||
if (perror)
|
||||
*perror = MLHMMV_ERROR_KEYLIST_TOO_SHALLOW;
|
||||
return NULL;
|
||||
}
|
||||
mlhmmv_level_entry_t* plevel_entry = mlhmmv_level_get_next_level_entry(plevel, &prestkeys->value, NULL);
|
||||
while (prestkeys->pnext != NULL) {
|
||||
if (plevel_entry == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (plevel_entry->level_value.is_terminal) {
|
||||
if (perror)
|
||||
*perror = MLHMMV_ERROR_KEYLIST_TOO_DEEP;
|
||||
return NULL;
|
||||
}
|
||||
plevel = plevel_entry->level_value.pnext_level;
|
||||
prestkeys = prestkeys->pnext;
|
||||
plevel_entry = mlhmmv_level_get_next_level_entry(plevel_entry->level_value.pnext_level, &prestkeys->value, NULL);
|
||||
}
|
||||
return plevel_entry;
|
||||
}
|
||||
|
||||
static mlhmmv_level_t* mlhmmv_level_ref_or_create(mlhmmv_level_t* plevel, sllmve_t* prest_keys) {
|
||||
if ((plevel->num_occupied + plevel->num_freed) >= (plevel->array_length * LOAD_FACTOR))
|
||||
mlhmmv_level_enlarge(plevel);
|
||||
return mlhmmv_get_or_create_level_aux_no_enlarge(plevel, prest_keys);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static mlhmmv_level_t* mlhmmv_get_or_create_level_aux_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys) {
|
||||
mv_t* plevel_key = &prest_keys->value;
|
||||
int ideal_index = 0;
|
||||
|
|
@ -1364,6 +1567,7 @@ mlhmmv_level_t* mlhmmv_root_look_up_and_ref_level(mlhmmv_root_t* pmap, sllmv_t*
|
|||
return plevel_entry->level_value.pnext_level;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static mlhmmv_level_entry_t* mlhmmv_level_get_next_level_entry(mlhmmv_level_t* plevel, mv_t* plevel_key, int* pindex) {
|
||||
int ideal_index = 0;
|
||||
int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
|
||||
|
|
@ -1382,6 +1586,7 @@ static mlhmmv_level_entry_t* mlhmmv_level_get_next_level_entry(mlhmmv_level_t* p
|
|||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static mlhmmv_xvalue_t* mlhmmv_level_get_next_level_xvalue(mlhmmv_level_t* plevel, mv_t* plevel_key) {
|
||||
if (plevel == NULL)
|
||||
return NULL;
|
||||
|
|
@ -1391,205 +1596,3 @@ static mlhmmv_xvalue_t* mlhmmv_level_get_next_level_xvalue(mlhmmv_level_t* pleve
|
|||
else
|
||||
return &pentry->level_value;
|
||||
}
|
||||
|
||||
static void mlhmmv_level_put_xvalue_no_enlarge(mlhmmv_level_t* plevel, sllmve_t* prest_keys,
|
||||
mlhmmv_xvalue_t* pvalue)
|
||||
{
|
||||
mv_t* plevel_key = &prest_keys->value;
|
||||
int ideal_index = 0;
|
||||
int index = mlhmmv_level_find_index_for_key(plevel, plevel_key, &ideal_index);
|
||||
mlhmmv_level_entry_t* pentry = &plevel->entries[index];
|
||||
|
||||
if (plevel->states[index] == EMPTY) { // End of chain.
|
||||
pentry->ideal_index = ideal_index;
|
||||
pentry->level_key = mv_copy(plevel_key);
|
||||
|
||||
if (prest_keys->pnext == NULL) {
|
||||
pentry->level_value = *pvalue;
|
||||
} else {
|
||||
pentry->level_value.is_terminal = FALSE;
|
||||
pentry->level_value.pnext_level = mlhmmv_level_alloc();
|
||||
}
|
||||
plevel->states[index] = OCCUPIED;
|
||||
|
||||
if (plevel->phead == NULL) { // First entry at this level
|
||||
pentry->pprev = NULL;
|
||||
pentry->pnext = NULL;
|
||||
plevel->phead = pentry;
|
||||
plevel->ptail = pentry;
|
||||
} else { // Subsequent entry at this level
|
||||
pentry->pprev = plevel->ptail;
|
||||
pentry->pnext = NULL;
|
||||
plevel->ptail->pnext = pentry;
|
||||
plevel->ptail = pentry;
|
||||
}
|
||||
|
||||
plevel->num_occupied++;
|
||||
if (prest_keys->pnext != NULL) {
|
||||
// RECURSE
|
||||
mlhmmv_level_put_xvalue(pentry->level_value.pnext_level, prest_keys->pnext, pvalue);
|
||||
}
|
||||
|
||||
} else if (plevel->states[index] == OCCUPIED) { // Existing key found in chain
|
||||
if (prest_keys->pnext == NULL) { // Place the terminal at this level
|
||||
if (pentry->level_value.is_terminal) {
|
||||
mv_free(&pentry->level_value.terminal_mlrval);
|
||||
} else {
|
||||
mlhmmv_level_free(pentry->level_value.pnext_level);
|
||||
}
|
||||
pentry->level_value = *pvalue;
|
||||
|
||||
} else { // The terminal will be placed at a deeper level
|
||||
if (pentry->level_value.is_terminal) {
|
||||
mv_free(&pentry->level_value.terminal_mlrval);
|
||||
pentry->level_value.is_terminal = FALSE;
|
||||
pentry->level_value.pnext_level = mlhmmv_level_alloc();
|
||||
}
|
||||
// RECURSE
|
||||
mlhmmv_level_put_xvalue(pentry->level_value.pnext_level, prest_keys->pnext, pvalue);
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "%s: mlhmmv_level_find_index_for_key did not find end of chain\n", MLR_GLOBALS.bargv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
static void mlhmmv_level_enlarge(mlhmmv_level_t* plevel) {
|
||||
mlhmmv_level_entry_t* old_entries = plevel->entries;
|
||||
mlhmmv_level_entry_state_t* old_states = plevel->states;
|
||||
mlhmmv_level_entry_t* old_head = plevel->phead;
|
||||
|
||||
mlhmmv_level_init(plevel, plevel->array_length*ENLARGEMENT_FACTOR);
|
||||
|
||||
for (mlhmmv_level_entry_t* pentry = old_head; pentry != NULL; pentry = pentry->pnext) {
|
||||
mlhmmv_level_move(plevel, &pentry->level_key, &pentry->level_value);
|
||||
}
|
||||
free(old_entries);
|
||||
free(old_states);
|
||||
}
|
||||
|
||||
static void 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)
|
||||
{
|
||||
if (prestnames != NULL) {
|
||||
// If there is a namelist entry, pull it out to its own field on the output lrecs.
|
||||
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
|
||||
mlhmmv_xvalue_t* plevel_value = &pe->level_value;
|
||||
lrec_t* pnextrec = lrec_copy(ptemplate);
|
||||
lrec_put(pnextrec,
|
||||
mv_alloc_format_val(&prestnames->value),
|
||||
mv_alloc_format_val(&pe->level_key), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
if (plevel_value->is_terminal) {
|
||||
lrec_put(pnextrec,
|
||||
mlr_strdup_or_die(prefix),
|
||||
mv_alloc_format_val(&plevel_value->terminal_mlrval), FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
sllv_append(poutrecs, pnextrec);
|
||||
} else {
|
||||
mlhmmv_level_to_lrecs_across_records(pe->level_value.pnext_level,
|
||||
prefix, prestnames->pnext, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
|
||||
lrec_free(pnextrec);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// If there are no more remaining namelist entries, flatten remaining map levels using the join separator
|
||||
// (default ":") and use them to create lrec values.
|
||||
lrec_t* pnextrec = lrec_copy(ptemplate);
|
||||
int emit = TRUE;
|
||||
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
|
||||
mlhmmv_xvalue_t* plevel_value = &pe->level_value;
|
||||
if (plevel_value->is_terminal) {
|
||||
char* temp = mv_alloc_format_val(&pe->level_key);
|
||||
char* next_prefix = do_full_prefixing
|
||||
? mlr_paste_3_strings(prefix, flatten_separator, temp)
|
||||
: mlr_strdup_or_die(temp);
|
||||
free(temp);
|
||||
lrec_put(pnextrec,
|
||||
next_prefix,
|
||||
mv_alloc_format_val(&plevel_value->terminal_mlrval),
|
||||
FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
} else if (do_full_prefixing) {
|
||||
char* temp = mv_alloc_format_val(&pe->level_key);
|
||||
char* next_prefix = mlr_paste_3_strings(prefix, flatten_separator, temp);
|
||||
free(temp);
|
||||
mlhmmv_level_to_lrec_within_record(plevel_value->pnext_level, next_prefix, pnextrec,
|
||||
do_full_prefixing, flatten_separator);
|
||||
free(next_prefix);
|
||||
} else {
|
||||
mlhmmv_level_to_lrecs_across_records(pe->level_value.pnext_level,
|
||||
prefix, NULL, pnextrec, poutrecs, do_full_prefixing, flatten_separator);
|
||||
emit = FALSE;
|
||||
}
|
||||
}
|
||||
if (emit)
|
||||
sllv_append(poutrecs, pnextrec);
|
||||
else
|
||||
lrec_free(pnextrec);
|
||||
}
|
||||
}
|
||||
|
||||
static void mlhmmv_level_to_lrec_within_record(
|
||||
mlhmmv_level_t* plevel,
|
||||
char* prefix,
|
||||
lrec_t* poutrec,
|
||||
int do_full_prefixing,
|
||||
char* flatten_separator)
|
||||
{
|
||||
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
|
||||
mlhmmv_xvalue_t* plevel_value = &pe->level_value;
|
||||
char* temp = mv_alloc_format_val(&pe->level_key);
|
||||
char* next_prefix = do_full_prefixing
|
||||
? mlr_paste_3_strings(prefix, flatten_separator, temp)
|
||||
: mlr_strdup_or_die(temp);
|
||||
free(temp);
|
||||
if (plevel_value->is_terminal) {
|
||||
lrec_put(poutrec,
|
||||
next_prefix,
|
||||
mv_alloc_format_val(&plevel_value->terminal_mlrval),
|
||||
FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
|
||||
} else {
|
||||
mlhmmv_level_to_lrec_within_record(plevel_value->pnext_level, next_prefix, poutrec,
|
||||
do_full_prefixing, flatten_separator);
|
||||
free(next_prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
typedef int mlhmmv_typed_hash_func(mv_t* pa);
|
||||
|
||||
static int mlhmmv_string_hash_func(mv_t* pa) {
|
||||
return mlr_string_hash_func(pa->u.strv);
|
||||
}
|
||||
static int mlhmmv_int_hash_func(mv_t* pa) {
|
||||
return pa->u.intv;
|
||||
}
|
||||
static int mlhmmv_other_hash_func(mv_t* pa) {
|
||||
fprintf(stderr, "%s: map keys must be of type %s or %s; got %s.\n",
|
||||
MLR_GLOBALS.bargv0,
|
||||
mt_describe_type(MT_STRING),
|
||||
mt_describe_type(MT_INT),
|
||||
mt_describe_type(pa->type));
|
||||
exit(1);
|
||||
}
|
||||
static mlhmmv_typed_hash_func* mlhmmv_hash_func_dispositions[MT_DIM] = {
|
||||
/*ERROR*/ mlhmmv_other_hash_func,
|
||||
/*ABSENT*/ mlhmmv_other_hash_func,
|
||||
/*EMPTY*/ mlhmmv_other_hash_func,
|
||||
/*STRING*/ mlhmmv_string_hash_func,
|
||||
/*INT*/ mlhmmv_int_hash_func,
|
||||
/*FLOAT*/ mlhmmv_other_hash_func,
|
||||
/*BOOL*/ mlhmmv_other_hash_func,
|
||||
};
|
||||
|
||||
static int mlhmmv_hash_func(mv_t* pa) {
|
||||
return mlhmmv_hash_func_dispositions[pa->type](pa);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ typedef struct _mlhmmv_level_t {
|
|||
} mlhmmv_level_t;
|
||||
|
||||
mlhmmv_level_t* mlhmmv_level_alloc();
|
||||
// xxx need to expose level-free
|
||||
void mlhmmv_level_free(mlhmmv_level_t* plevel);
|
||||
|
||||
void mlhmmv_level_clear(mlhmmv_level_t* plevel);
|
||||
|
||||
|
|
@ -242,14 +242,14 @@ void mlhmmv_root_remove(mlhmmv_root_t* pmap, sllmv_t* pmvkeys);
|
|||
// a sum:wye
|
||||
// hat 0.031442
|
||||
|
||||
// For 'emit' and 'emitp' in the DSL
|
||||
void 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);
|
||||
|
||||
// For 'emit all' and 'emitp all' in the DSL
|
||||
void mlhmmv_root_all_to_lrecs(mlhmmv_root_t* pmap, sllmv_t* pnames, sllv_t* poutrecs,
|
||||
int do_full_prefixing, char* flatten_separator);
|
||||
|
||||
// For 'emit' and 'emitp' in the DSL
|
||||
void 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);
|
||||
|
||||
// For 'dump' in the DSL; also used by the lrec-to-JSON writer.
|
||||
void mlhmmv_root_print_json_stacked(mlhmmv_root_t* pmap, int quote_values_always, char* line_indent, FILE* ostream);
|
||||
void mlhmmv_root_print_json_single_lines(mlhmmv_root_t* pmap, int quote_values_always, FILE* ostream);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue