Commit 6d6bc9ef authored by Damien George's avatar Damien George
Browse files

Merge pull request #108 from chipaca/dict_feats

Dictionary features that don't involve views or classmethods. First part of issue #99.
parents dfc0bac0 baa66540
......@@ -9,7 +9,8 @@
#include "map.h"
// approximatelly doubling primes; made with Mathematica command: Table[Prime[Floor[(1.7)^n]], {n, 3, 24}]
static int doubling_primes[] = {7, 19, 43, 89, 179, 347, 647, 1229, 2297, 4243, 7829, 14347, 26017, 47149, 84947, 152443, 273253, 488399, 869927, 1547173, 2745121, 4861607};
// prefixed with zero for the empty case.
static int doubling_primes[] = {0, 7, 19, 43, 89, 179, 347, 647, 1229, 2297, 4243, 7829, 14347, 26017, 47149, 84947, 152443, 273253, 488399, 869927, 1547173, 2745121, 4861607};
int get_doubling_prime_greater_or_equal_to(int x) {
for (int i = 0; i < sizeof(doubling_primes) / sizeof(int); i++) {
......@@ -38,7 +39,32 @@ mp_map_t *mp_map_new(mp_map_kind_t kind, int n) {
return map;
}
mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found) {
void mp_map_clear(mp_map_t *map) {
map->used = 0;
machine_uint_t a = map->alloc;
map->alloc = 0;
map->table = m_renew(mp_map_elem_t, map->table, a, map->alloc);
mp_map_elem_t nul = {NULL, NULL};
for (uint i=0; i<map->alloc; i++) {
map->table[i] = nul;
}
}
static void mp_map_rehash (mp_map_t *map) {
int old_alloc = map->alloc;
mp_map_elem_t *old_table = map->table;
map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
map->used = 0;
map->table = m_new0(mp_map_elem_t, map->alloc);
for (int i = 0; i < old_alloc; i++) {
if (old_table[i].key != NULL) {
mp_map_lookup_helper(map, old_table[i].key, true, false)->value = old_table[i].value;
}
}
m_del(mp_map_elem_t, old_table, old_alloc);
}
mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found, bool remove_if_found) {
bool is_map_mp_obj = (map->kind == MP_MAP_OBJ);
machine_uint_t hash;
if (is_map_mp_obj) {
......@@ -46,6 +72,13 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n
} else {
hash = (machine_uint_t)index;
}
if (map->alloc == 0) {
if (add_if_not_found) {
mp_map_rehash(map);
} else {
return NULL;
}
}
uint pos = hash % map->alloc;
for (;;) {
mp_map_elem_t *elem = &map->table[pos];
......@@ -54,17 +87,7 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n
if (add_if_not_found) {
if (map->used + 1 >= map->alloc) {
// not enough room in table, rehash it
int old_alloc = map->alloc;
mp_map_elem_t *old_table = map->table;
map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
map->used = 0;
map->table = m_new0(mp_map_elem_t, map->alloc);
for (int i = 0; i < old_alloc; i++) {
if (old_table[i].key != NULL) {
mp_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value;
}
}
m_del(mp_map_elem_t, old_table, old_alloc);
mp_map_rehash(map);
// restart the search for the new element
pos = hash % map->alloc;
} else {
......@@ -82,6 +105,16 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n
elem->key = index;
}
*/
if (remove_if_found) {
map->used--;
/* this leaks this memory (but see dict_get_helper) */
mp_map_elem_t *retval = m_new(mp_map_elem_t, 1);
retval->key = elem->key;
retval->value = elem->value;
elem->key = NULL;
elem->value = NULL;
return retval;
}
return elem;
} else {
// not yet found, keep searching in this table
......@@ -92,7 +125,7 @@ mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_n
mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found) {
mp_obj_t o = (mp_obj_t)(machine_uint_t)index;
return mp_map_lookup_helper(map, o, add_if_not_found);
return mp_map_lookup_helper(map, o, add_if_not_found, false);
}
/******************************************************************************/
......@@ -106,6 +139,7 @@ void mp_set_init(mp_set_t *set, int n) {
mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found) {
int hash = mp_obj_hash(index);
assert(set->alloc); /* FIXME: if alloc is ever 0 when doing a lookup, this'll fail: */
int pos = hash % set->alloc;
for (;;) {
mp_obj_t elem = set->table[pos];
......
......@@ -26,8 +26,9 @@ typedef struct _mp_set_t {
int get_doubling_prime_greater_or_equal_to(int x);
void mp_map_init(mp_map_t *map, mp_map_kind_t kind, int n);
mp_map_t *mp_map_new(mp_map_kind_t kind, int n);
mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found);
mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found, bool remove_if_found);
mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found);
void mp_map_clear(mp_map_t *map);
void mp_set_init(mp_set_t *set, int n);
mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found);
......@@ -17,20 +17,23 @@ typedef struct _mp_obj_dict_t {
mp_map_t map;
} mp_obj_dict_t;
static mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur);
static mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in);
static void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
mp_obj_dict_t *self = self_in;
bool first = true;
print(env, "{");
for (int i = 0; i < self->map.alloc; i++) {
if (self->map.table[i].key != NULL) {
if (!first) {
print(env, ", ");
}
first = false;
mp_obj_print_helper(print, env, self->map.table[i].key);
print(env, ": ");
mp_obj_print_helper(print, env, self->map.table[i].value);
mp_obj_t *dict_iter = mp_obj_new_dict_iterator(self, 0);
mp_map_elem_t *next = NULL;
while ((next = dict_it_iternext_elem(dict_iter)) != NULL) {
if (!first) {
print(env, ", ");
}
first = false;
mp_obj_print_helper(print, env, next->key);
print(env, ": ");
mp_obj_print_helper(print, env, next->value);
}
print(env, "}");
}
......@@ -47,7 +50,7 @@ static mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
case RT_BINARY_OP_SUBSCR:
{
// dict load
mp_map_elem_t *elem = mp_map_lookup_helper(&o->map, rhs_in, false);
mp_map_elem_t *elem = mp_map_lookup_helper(&o->map, rhs_in, false, false);
if (elem == NULL) {
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "<value>"));
} else {
......@@ -60,12 +63,211 @@ static mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
}
}
/******************************************************************************/
/* dict iterator */
typedef struct _mp_obj_dict_it_t {
mp_obj_base_t base;
mp_obj_dict_t *dict;
machine_uint_t cur;
} mp_obj_dict_it_t;
static mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in) {
mp_obj_dict_it_t *self = self_in;
machine_uint_t max = self->dict->map.alloc;
mp_map_elem_t *table = self->dict->map.table;
for (int i = self->cur; i < max; i++) {
if (table[i].key != NULL) {
self->cur = i + 1;
return &(table[i]);
}
}
return NULL;
}
mp_obj_t dict_it_iternext(mp_obj_t self_in) {
mp_map_elem_t *next = dict_it_iternext_elem(self_in);
if (next != NULL) {
return next->key;
} else {
return mp_const_stop_iteration;
}
}
static const mp_obj_type_t dict_it_type = {
{ &mp_const_type },
"dict_iterator",
.iternext = dict_it_iternext,
};
static mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur) {
mp_obj_dict_it_t *o = m_new_obj(mp_obj_dict_it_t);
o->base.type = &dict_it_type;
o->dict = dict;
o->cur = cur;
return o;
}
static mp_obj_t dict_getiter(mp_obj_t o_in) {
return mp_obj_new_dict_iterator(o_in, 0);
}
/******************************************************************************/
/* dict methods */
static mp_obj_t dict_clear(mp_obj_t self_in) {
assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
mp_obj_dict_t *self = self_in;
mp_map_clear(&self->map);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear);
static mp_obj_t dict_copy(mp_obj_t self_in) {
assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
mp_obj_dict_t *self = self_in;
mp_obj_dict_t *other = mp_obj_new_dict(self->map.alloc);
other->map.used = self->map.used;
memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t));
return other;
}
static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy);
static mp_obj_t dict_get_helper(mp_map_t *self, mp_obj_t key, mp_obj_t deflt, bool pop, bool set) {
mp_map_elem_t *elem = mp_map_lookup_helper(self, key, set, pop);
mp_obj_t value;
if (elem == NULL || elem->value == NULL) {
if (deflt == NULL) {
if (pop) {
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "<value>"));
} else {
value = mp_const_none;
}
} else {
value = deflt;
}
} else {
value = elem->value;
if (pop) {
/* catch the leak (from mp_map_lookup_helper) */
m_free(elem, sizeof(mp_map_elem_t));
}
}
if (set) {
elem->value = value;
}
return value;
}
static mp_obj_t dict_get(int n_args, const mp_obj_t *args) {
assert(2 <= n_args && n_args <= 3);
assert(MP_OBJ_IS_TYPE(args[0], &dict_type));
return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
args[1],
n_args == 3 ? args[2] : NULL,
false, false);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_get_obj, 2, 3, dict_get);
static mp_obj_t dict_pop(int n_args, const mp_obj_t *args) {
assert(2 <= n_args && n_args <= 3);
assert(MP_OBJ_IS_TYPE(args[0], &dict_type));
return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
args[1],
n_args == 3 ? args[2] : NULL,
true, false);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_pop_obj, 2, 3, dict_pop);
static mp_obj_t dict_setdefault(int n_args, const mp_obj_t *args) {
assert(2 <= n_args && n_args <= 3);
assert(MP_OBJ_IS_TYPE(args[0], &dict_type));
return dict_get_helper(&((mp_obj_dict_t *)args[0])->map,
args[1],
n_args == 3 ? args[2] : NULL,
false, true);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setdefault);
static mp_obj_t dict_popitem(mp_obj_t self_in) {
assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
mp_obj_dict_t *self = self_in;
if (self->map.used == 0) {
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_KeyError, "popitem(): dictionary is empty"));
}
mp_obj_dict_it_t *iter = mp_obj_new_dict_iterator(self, 0);
mp_map_elem_t *next = dict_it_iternext_elem(iter);
self->map.used--;
mp_obj_t items[] = {next->key, next->value};
next->key = NULL;
next->value = NULL;
mp_obj_t tuple = mp_obj_new_tuple(2, items);
return tuple;
}
static MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem);
static mp_obj_t dict_update(mp_obj_t self_in, mp_obj_t iterable) {
assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
mp_obj_dict_t *self = self_in;
/* TODO: check for the "keys" method */
mp_obj_t iter = rt_getiter(iterable);
mp_obj_t next = NULL;
while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
mp_obj_t inneriter = rt_getiter(next);
mp_obj_t key = rt_iternext(inneriter);
mp_obj_t value = rt_iternext(inneriter);
mp_obj_t stop = rt_iternext(inneriter);
if (key == mp_const_stop_iteration
|| value == mp_const_stop_iteration
|| stop != mp_const_stop_iteration) {
nlr_jump(mp_obj_new_exception_msg(
MP_QSTR_ValueError,
"dictionary update sequence has the wrong length"));
} else {
mp_map_lookup_helper(&self->map, key, true, false)->value = value;
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_2(dict_update_obj, dict_update);
/******************************************************************************/
/* dict constructors & etc */
static const mp_method_t dict_type_methods[] = {
{ "clear", &dict_clear_obj },
{ "copy", &dict_copy_obj },
{ "get", &dict_get_obj },
{ "pop", &dict_pop_obj },
{ "popitem", &dict_popitem_obj },
{ "setdefault", &dict_setdefault_obj },
{ "update", &dict_update_obj },
{ NULL, NULL }, // end-of-list sentinel
};
const mp_obj_type_t dict_type = {
{ &mp_const_type },
"dict",
.print = dict_print,
.make_new = dict_make_new,
.binary_op = dict_binary_op,
.getiter = dict_getiter,
.methods = dict_type_methods,
};
mp_obj_t mp_obj_new_dict(int n_args) {
......@@ -76,19 +278,12 @@ mp_obj_t mp_obj_new_dict(int n_args) {
}
uint mp_obj_dict_len(mp_obj_t self_in) {
mp_obj_dict_t *self = self_in;
uint len = 0;
for (int i = 0; i < self->map.alloc; i++) {
if (self->map.table[i].key != NULL) {
len += 1;
}
}
return len;
return ((mp_obj_dict_t *)self_in)->map.used;
}
mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) {
assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
mp_obj_dict_t *self = self_in;
mp_map_lookup_helper(&self->map, key, true)->value = value;
mp_map_lookup_helper(&self->map, key, true, false)->value = value;
return self_in;
}
......@@ -879,13 +879,13 @@ mp_obj_t rt_getiter(mp_obj_t o_in) {
mp_obj_t rt_iternext(mp_obj_t o_in) {
if (MP_OBJ_IS_SMALL_INT(o_in)) {
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "? 'int' object is not iterable"));
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "'int' object is not an iterator"));
} else {
mp_obj_base_t *o = o_in;
if (o->type->iternext != NULL) {
return o->type->iternext(o_in);
} else {
nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "? '%s' object is not iterable", o->type->name));
nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_TypeError, "'%s' object is not an iterator", o->type->name));
}
}
}
......
d = {1: 2, 3: 4}
print(d)
d.clear()
print(d)
d[2] = 42
print(d)
a = {i: 2*i for i in range(1000)}
b = a.copy()
for i in range(1000):
print(i, b[i])
print(len(b))
for d in {}, {42:2}:
print(d.get(42))
print(d.get(42,2))
d = {1: 2, 3: 4}
for i in d:
print(i, d[i])
d = {1: 2, 3: 4}
print(d.pop(3), d)
print(d)
print(d.pop(1, 42), d)
print(d.pop(1, 42), d)
print(d.pop(1, None), d)
try:
print(d.pop(1), "!!!",)
except KeyError:
print("Raised KeyError")
else:
print("Did not rise KeyError!")
d={1:2,3:4}
print(d.popitem())
print(d)
print(d.popitem())
print(d)
try:
print(d.popitem(), "!!!",)
except KeyError:
print("Raised KeyError")
else:
print("Did not raise KeyError")
d = {}
print(d.setdefault(1))
print(d.setdefault(1))
print(d.setdefault(5, 42))
print(d.setdefault(5, 1))
print(d[1])
print(d[5])
d.pop(5)
print(d.setdefault(5, 1))
print(d[1])
print(d[5])
d = {1:2, 3:4}
print(d)
d.update(["ab"])
print(d[1])
print(d[3])
print(d["a"])
print(len(d))
d.update([(1,4)])
print(d[1])
print(len(d))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment