mcxHash* mcxHashNew ( dim n_buckets , u32 (*hash)(const void *a) , int (*cmp) (const void *a, const void *b) ) { mcxHash *h ; mcxbool ok = FALSE ; u8 n_bits = 0 ; if (!n_buckets) { mcxErr("mcxHashNew strange", "void alloc request") ; n_buckets = 2 ; } if (!(h = mcxAlloc(sizeof(mcxHash), RETURN_ON_FAIL))) return NULL ; while(n_buckets) { n_buckets >>= 1 ; n_bits++ ; } h->load = 0.5 ; h->n_entries = 0 ; h->n_buckets = n_buckets = (1 << n_bits) ; h->cmp = cmp ; h->hash = hash ; h->options = MCX_HASH_OPT_DEFAULTS ; h->src_link = NULL ; while (1) /* fixme 2nd arg below, have better choice? */ { h->src_link = mcxGrimNew(sizeof(hash_link), h->n_buckets, MCX_GRIM_ARITHMETIC) ; if (!h->src_link) break ; if (! ( h->buckets = mcxNAlloc ( h->n_buckets , sizeof(mcx_bucket) , mcx_bucket_init , RETURN_ON_FAIL ) ) ) break ; ok = TRUE ; break ; } if (!ok) { mcxGrimFree(&(h->src_link)) ; mcxFree(h) ; return NULL ; } return h ; }
static dim do_a_file ( aggr** collectpp , const char* fname , dim collect_n ) { mcxIO* xf = mcxIOnew(fname, "r") ; mcxTing* buf = mcxTingEmpty(NULL, 100) ; mcxTing* lbl = mcxTingEmpty(NULL, 100) ; mcxstatus status = STATUS_OK ; aggr* collect = *collectpp ; dim ct = 0, collect_alloc = 0 ; if (!collect_n) collect_alloc = 100 , collect = mcxNAlloc(collect_alloc, sizeof collect[0], NULL, EXIT_ON_FAIL) ; mcxIOopen(xf, EXIT_ON_FAIL) ; while (STATUS_OK == (status = mcxIOreadLine(xf, buf, MCX_READLINE_CHOMP))) { double val ; const char* tabchar = NULL ; mcxbool get_header = collect_g != 'p' && !ct ? TRUE : FALSE ; mcxTingEnsure(lbl, buf->len) /* if header_g && !ct && !paste create/check label */ /* body of this while loop does too many things, refactor */ ; if (collect_g == 'p' || get_header) { if (!(tabchar = strchr(buf->str, '\t'))) mcxDie(1, me, "paste error at line %d file %s (no tab)", (int) xf->lc, fname) ; mcxTingNWrite(lbl, buf->str, tabchar - buf->str) ; } else { if (2 != sscanf(buf->str, "%s%lg", lbl->str, &val)) mcxDie(1, me, "parse error at line %d file %s", (int) xf->lc, fname) ; lbl->len = strlen(lbl->str) ; } if (!collect_n) { if (ct >= collect_alloc) { dim collect_realloc = collect_alloc * 1.44 ; collect = mcxNRealloc(collect, collect_realloc, collect_alloc, sizeof collect[0], NULL, EXIT_ON_FAIL) ; collect_alloc = collect_realloc ; } collect[ct].label = mcxTingStr(lbl) ; collect[ct].val = collect_g == 'p' || get_header ? 0.0 : val ; collect[ct].columns = collect_g == 'p' || get_header ? mcxTingNew(tabchar + (get_header ? 1 : 0)) : NULL ; } else { if (ct >= collect_n) mcxDie(1, me, "additional lines in file %s", fname) ; if (strcmp(collect[ct].label, lbl->str)) mcxDie ( 1 , me , "label conflict %s/%s at line %d in file %s" , collect[ct].label , lbl->str , (int) xf->lc, fname ) ; if (get_header) /* only need to check identity */ { if (strcmp(tabchar+1, collect[ct].columns->str)) mcxDie(1, me, "different columns <%s> and <%s>", collect[ct].columns->str, tabchar+1) ; } else if (collect_g == 'p') /* tack it on */ mcxTingNAppend(collect[ct].columns, tabchar, buf->len - lbl->len) ; else collect[ct].val += val ; } ct++ ; } if (collect_n) { if (ct != collect_n) mcxDie(1, me, "not enough lines in file %s", fname) ; } else { if (!ct) mcxDie(1, me, "empty file(s)") ; *collectpp = collect ; } mcxIOfree(&xf) ; return ct ; }