/// Clear out the raw set and cooked sets. Destroy the entire /// cooked data structure but leave the raw one present though /// empty. This is the same as the initial (post-creation) state. /// @param[in] ca the object pointer void ca_clear_pa(ca_o ca) { dict_t *dict; dnode_t *dnp, *next; pa_o pa; if ((dict = ca->ca_raw_pa_dict)) { for (dnp = dict_first(dict); dnp;) { next = dict_next(dict, dnp); pa = (pa_o)dnode_getkey(dnp); dict_delete(dict, dnp); dnode_destroy(dnp); pa_destroy(pa); dnp = next; } } if ((dict = ca->ca_cooked_pa_dict)) { for (dnp = dict_first(dict); dnp;) { next = dict_next(dict, dnp); pa = (pa_o)dnode_getkey(dnp); dict_delete(dict, dnp); dnode_destroy(dnp); pa_destroy(pa); dnp = next; } dict_destroy(ca->ca_cooked_pa_dict); ca->ca_cooked_pa_dict = NULL; } }
void * dlist_remove_back(struct dlist *l) { if (l->counter) { struct dnode *remove = l->back; l->back = l->back->prev; if (l->back) l->back->next = NULL; l->counter--; return dnode_destroy(remove); } else { return NULL; } }
void * dlist_remove_front(struct dlist *l) { if (l->counter) { struct dnode *remove = l->front; l->front = l->front->next; if (l->front) l->front->prev = NULL; // account for case with // 1 element in list l->counter--; return dnode_destroy(remove); } else { return NULL; } }
/// Merge two CmdActions, a 'leader' and a 'donor', together. The donor /// is then redundant. /// @param[in] leader the leader object pointer /// @param[in] donor the donor object pointer void ca_merge(ca_o leader, ca_o donor) { CCS sub; dict_t *dict; dnode_t *dnp, *next; pa_o pa; // ASSUMPTION: aggregated CAs are composed from // commands run serially - i.e. any parallelism // is above the build-script level. // Stringify the donor and leave that string representation in // the leader for the record. sub = ca_format_header(donor); if (leader->ca_subs) { leader->ca_subs = (CCS)putil_realloc((void *)leader->ca_subs, (strlen(leader->ca_subs) + strlen(sub) + 1) * CHARSIZE); strcat((CS)leader->ca_subs, sub); } else { leader->ca_subs = (CCS)putil_malloc((strlen(sub) + 1) * CHARSIZE); strcpy((CS)leader->ca_subs, sub); } putil_free(sub); // Traverse the donor's raw list of PA's and move them into the leader's. if ((dict = donor->ca_raw_pa_dict)) { for (dnp = dict_first(dict); dnp;) { next = dict_next(dict, dnp); pa = (pa_o)dnode_getkey(dnp); ca_record_pa(leader, pa); dict_delete(dict, dnp); dnode_destroy(dnp); dnp = next; } dict_destroy(donor->ca_raw_pa_dict); donor->ca_raw_pa_dict = NULL; } }
static void construct(dict_t *d) { input_t in; int done = 0; dict_load_t dl; dnode_t *dn; char *tok1, *tok2, *val; const char *key; char *help = "p turn prompt on\n" "q finish construction\n" "a <key> <val> add new entry\n"; if (!dict_isempty(d)) puts("warning: dictionary not empty!"); dict_load_begin(&dl, d); while (!done) { if (prompt) putchar('>'); fflush(stdout); if (!fgets(in, sizeof(input_t), stdin)) break; switch (in[0]) { case '?': puts(help); break; case 'p': prompt = 1; break; case 'q': done = 1; break; case 'a': if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { puts("what?"); break; } key = dupstring(tok1); val = dupstring(tok2); dn = dnode_create(val); if (!key || !val || !dn) { puts("out of memory"); free((void *) key); free(val); if (dn) dnode_destroy(dn); } dict_load_next(&dl, dn, key); break; default: putchar('?'); putchar('\n'); break; } } dict_load_end(&dl); }
/// Convert all raw PAs in the group into a single set of "cooked" PAs /// under the leader. /// @param[in] ca the object pointer void ca_coalesce(ca_o ca) { dict_t *dict_raw, *dict_cooked; dnode_t *dnpr, *dnpc, *next; dict_raw = ca->ca_raw_pa_dict; assert(!ca->ca_cooked_pa_dict); if ((dict_cooked = dict_create(DICTCOUNT_T_MAX, pa_cmp_by_pathname))) { ca->ca_cooked_pa_dict = dict_cooked; } else { putil_syserr(2, "dict_create()"); } for (dnpr = dict_first(dict_raw); dnpr;) { pa_o raw_pa, ckd_pa; next = dict_next(dict_raw, dnpr); raw_pa = (pa_o)dnode_getkey(dnpr); _ca_verbosity_pa(raw_pa, ca, "COALESCING"); // All data is in the key - that's why the value can be null. if ((dnpc = dict_lookup(dict_cooked, raw_pa))) { int keep_cooked = 0; ckd_pa = (pa_o)dnode_getkey(dnpc); if (!pa_is_read(raw_pa) && !pa_is_read(ckd_pa)) { moment_s raw_timestamp, ckd_timestamp; // If they're both destructive ops (non-read) then we // need to consider timestamps and use the later one. if (pa_has_timestamp(raw_pa) && pa_has_timestamp(ckd_pa)) { // If the PA's have their own timestamps, use them. raw_timestamp = pa_get_timestamp(raw_pa); ckd_timestamp = pa_get_timestamp(ckd_pa); } else { // Otherwise key off the file times. This is for // support of "dummy" PAs as used in shopping. raw_timestamp = pa_get_moment(raw_pa); ckd_timestamp = pa_get_moment(ckd_pa); } if (moment_cmp(raw_timestamp, ckd_timestamp, NULL) <= 0) { // Cooked write op is newer and can stay. keep_cooked = 1; } } else if (pa_is_read(raw_pa)) { // There's no point replacing a read with another read, // so regardless of whether the current cooked PA is a // read or write, it can stay. keep_cooked = 1; } else { // A write always beats a read. } if (!keep_cooked) { dict_delete(dict_cooked, dnpc); dnode_destroy(dnpc); _ca_verbosity_pa(ckd_pa, ca, "REMOVING"); pa_destroy(ckd_pa); if (!(dnpc = dnode_create(NULL))) { putil_syserr(2, "dnode_create()"); } ckd_pa = pa_copy(raw_pa); dict_insert(dict_cooked, dnpc, ckd_pa); } } else { if (!(dnpc = dnode_create(NULL))) { putil_syserr(2, "dnode_create()"); } ckd_pa = pa_copy(raw_pa); dict_insert(dict_cooked, dnpc, ckd_pa); } // Clean up the raw set as we move PAs to the cooked one. dict_delete(dict_raw, dnpr); dnode_destroy(dnpr); dnpr = next; } return; }