<commit_before>#include "pone.h"

void pone_hash_mark(pone_val* val) {
    const char* k;
    pone_val* v;
    kh_foreach(val->as.hash.h, k, v, {
        pone_gc_mark_value(v);
    });
}

pone_val* pone_hash_copy(pone_world* world, pone_val* obj) {
    pone_val* retval = pone_hash_new(world);
    const char* k;
    pone_val* v;
    kh_foreach(obj->as.hash.h, k, v, {
        pone_hash_assign_key(world,
            retval,
            pone_str_new_strdup(world, k, strlen(k)),
            pone_val_copy(world, v));
    });
    return retval;
}

// TODO: delete key
// TODO: push key
// TODO: exists key

pone_val* pone_hash_new(pone_world* world) {
    pone_val* hv = pone_obj_alloc(world, PONE_HASH);
    hv->as.hash.h = kh_init(str);
    return hv;
}

pone_val* pone_hash_assign_keys(pone_world* world, pone_val* hash, pone_int_t n, ...) {
    va_list list;

    va_start(list, n);
    // we can optimize in case of `{a => 3}`. we can omit mortalize.
    for (pone_int_t i = 0; i < n; i += 2) {
        pone_val* k = va_arg(list, pone_val*);
        pone_val* v = va_arg(list, pone_val*);
        pone_hash_assign_key(world, hash, k, v);
    }
    va_end(list);

    return hash; // return itself
}

void pone_hash_free(pone_world* world, pone_val* val) {
    pone_hash* h = (pone_hash*)val;
    const char* k;
    pone_val* v;
    kh_foreach(h->h, k, v, {
        pone_free(world->universe, (void*)k); // k is strdupped.
    });
    kh_destroy(str, h->h);
}

void pone_hash_assign_key_c(pone_world* world, pone_val* hv, const char* key, pone_int_t key_len, pone_val* v) {
    assert(pone_type(hv) == PONE_HASH);
    int ret;
    const char* ks = pone_strdup(world, key, key_len);
    khint_t k = kh_put(str, ((pone_hash*)hv)->h, ks, &ret);
    if (ret == -1) {
        abort(); // TODO better error msg
    }
    kh_val(((pone_hash*)hv)->h, k) = v;
    ((pone_hash*)hv)->len++;
}

bool pone_hash_exists_c(pone_world* world, pone_val* hash, const char* name) {
    assert(pone_type(hash) == PONE_HASH);
    assert(hash->as.hash.h);

    khint_t k = kh_get(str, hash->as.hash.h, name);
    if (k != kh_end(hash->as.hash.h)) {
        return true;
    } else {
        return false;
    }
}

pone_val* pone_hash_at_key_c(pone_universe* universe, pone_val* hash, const char* name) {
    assert(pone_type(hash) == PONE_HASH);
    assert(hash->as.hash.h);

    khint_t k = kh_get(str, hash->as.hash.h, name);
    if (k != kh_end(hash->as.hash.h)) {
        return kh_val(hash->as.hash.h, k);
    } else {
        return pone_nil();
    }
}

void pone_hash_assign_key(pone_world* world, pone_val* hv, pone_val* k, pone_val* v) {
    k = pone_stringify(world, k);
    pone_hash_assign_key_c(world, hv, pone_str_ptr(k), pone_str_len(k), v);
}

size_t pone_hash_elems(pone_val* val) {
    assert(pone_type(val) == PONE_HASH);
    return ((pone_hash*)val)->len;
}

pone_val* pone_hash_keys(pone_world* world, pone_val* val) {
    assert(pone_type(val) == PONE_HASH);

    pone_hash* h = (pone_hash*)val;

    pone_val* retval = pone_ary_new(world, 0);

    const char* k;
    pone_val* v;
    kh_foreach(h->h, k, v, {
        pone_ary_push(world->universe, retval, pone_str_new_strdup(world, k, strlen(k)));
    });

    return retval;
}
<commit_msg>free hash key if it's already registered.<commit_after>#include "pone.h"

void pone_hash_mark(pone_val* val) {
    const char* k;
    pone_val* v;
    kh_foreach(val->as.hash.h, k, v, {
        pone_gc_mark_value(v);
    });
}

pone_val* pone_hash_copy(pone_world* world, pone_val* obj) {
    pone_val* retval = pone_hash_new(world);
    const char* k;
    pone_val* v;
    kh_foreach(obj->as.hash.h, k, v, {
        pone_hash_assign_key(world,
            retval,
            pone_str_new_strdup(world, k, strlen(k)),
            pone_val_copy(world, v));
    });
    return retval;
}

// TODO: delete key
// TODO: push key
// TODO: exists key

pone_val* pone_hash_new(pone_world* world) {
    pone_val* hv = pone_obj_alloc(world, PONE_HASH);
    hv->as.hash.h = kh_init(str);
    return hv;
}

pone_val* pone_hash_assign_keys(pone_world* world, pone_val* hash, pone_int_t n, ...) {
    va_list list;

    va_start(list, n);
    // we can optimize in case of `{a => 3}`. we can omit mortalize.
    for (pone_int_t i = 0; i < n; i += 2) {
        pone_val* k = va_arg(list, pone_val*);
        pone_val* v = va_arg(list, pone_val*);
        pone_hash_assign_key(world, hash, k, v);
    }
    va_end(list);

    return hash; // return itself
}

void pone_hash_free(pone_world* world, pone_val* val) {
    pone_hash* h = (pone_hash*)val;
    const char* k;
    pone_val* v;
    kh_foreach(h->h, k, v, {
        pone_free(world->universe, (void*)k); // k is strdupped.
    });
    kh_destroy(str, h->h);
}

void pone_hash_assign_key_c(pone_world* world, pone_val* hv, const char* key, pone_int_t key_len, pone_val* v) {
    assert(pone_type(hv) == PONE_HASH);
    int ret;
    char* ks = pone_strdup(world, key, key_len);
    khint_t k = kh_put(str, ((pone_hash*)hv)->h, ks, &ret);
    if (ret == -1) {
        fprintf(stderr, "[BUG] khash.h returns error: %s\n", key);
        abort();
    } else if (ret == 0) {
        // the key is present in the hash table.
        pone_free(world->universe, ks);
    }
    kh_val(((pone_hash*)hv)->h, k) = v;
    ((pone_hash*)hv)->len++;
}

bool pone_hash_exists_c(pone_world* world, pone_val* hash, const char* name) {
    assert(pone_type(hash) == PONE_HASH);
    assert(hash->as.hash.h);

    khint_t k = kh_get(str, hash->as.hash.h, name);
    if (k != kh_end(hash->as.hash.h)) {
        return true;
    } else {
        return false;
    }
}

pone_val* pone_hash_at_key_c(pone_universe* universe, pone_val* hash, const char* name) {
    assert(pone_type(hash) == PONE_HASH);
    assert(hash->as.hash.h);

    khint_t k = kh_get(str, hash->as.hash.h, name);
    if (k != kh_end(hash->as.hash.h)) {
        return kh_val(hash->as.hash.h, k);
    } else {
        return pone_nil();
    }
}

void pone_hash_assign_key(pone_world* world, pone_val* hv, pone_val* k, pone_val* v) {
    k = pone_str_c_str(world, pone_stringify(world, k));
    pone_hash_assign_key_c(world, hv, pone_str_ptr(k), pone_str_len(k), v);
}

size_t pone_hash_elems(pone_val* val) {
    assert(pone_type(val) == PONE_HASH);
    return ((pone_hash*)val)->len;
}

pone_val* pone_hash_keys(pone_world* world, pone_val* val) {
    assert(pone_type(val) == PONE_HASH);

    pone_hash* h = (pone_hash*)val;

    pone_val* retval = pone_ary_new(world, 0);

    const char* k;
    pone_val* v;
    kh_foreach(h->h, k, v, {
        pone_ary_push(world->universe, retval, pone_str_new_strdup(world, k, strlen(k)));
    });

    return retval;
}

{'fraction_non_alphanumeric': 0.1543864893140039, 'fraction_numerical': 0.0016439994021820357, 'mean_word_length': 3.0656136087484813, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4836991', 'n_tokens_mistral': 2906, 'n_tokens_neox': 2751, 'n_words': 567}