diff options
Diffstat (limited to 'py/map.c')
-rw-r--r-- | py/map.c | 188 |
1 files changed, 126 insertions, 62 deletions
@@ -83,7 +83,7 @@ STATIC void mp_map_rehash(mp_map_t *map) { map->all_keys_are_qstrs = 1; map->table = m_new0(mp_map_elem_t, map->alloc); for (int i = 0; i < old_alloc; i++) { - if (old_table[i].key != NULL) { + if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) { mp_map_lookup(map, old_table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_table[i].value; } } @@ -106,8 +106,6 @@ mp_map_elem_t* mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t // map is a hash table (not a fixed array), so do a hash lookup - machine_uint_t hash; - hash = mp_obj_hash(index); if (map->alloc == 0) { if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_map_rehash(map); @@ -116,49 +114,78 @@ mp_map_elem_t* mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t } } + machine_uint_t hash = mp_obj_hash(index); uint pos = hash % map->alloc; + uint start_pos = pos; + mp_map_elem_t *avail_slot = NULL; for (;;) { - mp_map_elem_t *elem = &map->table[pos]; - if (elem->key == NULL) { - // not in table + mp_map_elem_t *slot = &map->table[pos]; + if (slot->key == MP_OBJ_NULL) { + // found NULL slot, so index is not in table if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { - if (map->used + 1 >= map->alloc) { - // not enough room in table, rehash it - mp_map_rehash(map); - // restart the search for the new element - pos = hash % map->alloc; - } else { - map->used += 1; - elem->key = index; - if (!MP_OBJ_IS_QSTR(index)) { - map->all_keys_are_qstrs = 0; - } - return elem; + map->used += 1; + if (avail_slot == NULL) { + avail_slot = slot; + } + slot->key = index; + slot->value = MP_OBJ_NULL; + if (!MP_OBJ_IS_QSTR(index)) { + map->all_keys_are_qstrs = 0; } + return slot; } else { - return NULL; + return MP_OBJ_NULL; } - } else if (elem->key == index || (!map->all_keys_are_qstrs && mp_obj_equal(elem->key, index))) { - // found it - /* it seems CPython does not replace the index; try x={True:'true'};x[1]='one';x - if (add_if_not_found) { - elem->key = index; + } else if (slot->key == MP_OBJ_SENTINEL) { + // found deleted slot, remember for later + if (avail_slot == NULL) { + avail_slot = slot; } - */ + } else if (slot->key == index || (!map->all_keys_are_qstrs && mp_obj_equal(slot->key, index))) { + // found index + // Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x if (lookup_kind & MP_MAP_LOOKUP_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; + retval->key = slot->key; + retval->value = slot->value; + // delete element in this slot + map->used--; + if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) { + // optimisation if next slot is empty + slot->key = MP_OBJ_NULL; + } else { + slot->key = MP_OBJ_SENTINEL; + } return retval; } - return elem; - } else { - // not yet found, keep searching in this table - pos = (pos + 1) % map->alloc; + return slot; + } + + // not yet found, keep searching in this table + pos = (pos + 1) % map->alloc; + + if (pos == start_pos) { + // search got back to starting position, so index is not in table + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot != NULL) { + // there was an available slot, so use that + map->used++; + avail_slot->key = index; + avail_slot->value = MP_OBJ_NULL; + if (!MP_OBJ_IS_QSTR(index)) { + map->all_keys_are_qstrs = 0; + } + return avail_slot; + } else { + // not enough room in table, rehash it + mp_map_rehash(map); + // restart the search for the new element + start_pos = pos = hash % map->alloc; + } + } else { + return MP_OBJ_NULL; + } } } } @@ -179,16 +206,14 @@ STATIC void mp_set_rehash(mp_set_t *set) { set->used = 0; set->table = m_new0(mp_obj_t, set->alloc); for (int i = 0; i < old_alloc; i++) { - if (old_table[i] != NULL) { - mp_set_lookup(set, old_table[i], true); + if (old_table[i] != MP_OBJ_NULL && old_table[i] != MP_OBJ_SENTINEL) { + mp_set_lookup(set, old_table[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); } } m_del(mp_obj_t, old_table, old_alloc); } mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { - int hash; - int pos; if (set->alloc == 0) { if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_set_rehash(set); @@ -196,45 +221,84 @@ mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t looku return NULL; } } - if (lookup_kind & MP_MAP_LOOKUP_FIRST) { - hash = 0; - pos = 0; - } else { - hash = mp_obj_hash(index);; - pos = hash % set->alloc; - } + machine_uint_t hash = mp_obj_hash(index); + uint pos = hash % set->alloc; + uint start_pos = pos; + mp_obj_t *avail_slot = NULL; for (;;) { mp_obj_t elem = set->table[pos]; if (elem == MP_OBJ_NULL) { - // not in table + // found NULL slot, so index is not in table if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { - if (set->used + 1 >= set->alloc) { + if (avail_slot == NULL) { + avail_slot = &set->table[pos]; + } + set->used++; + *avail_slot = index; + return index; + } else { + return MP_OBJ_NULL; + } + } else if (elem == MP_OBJ_SENTINEL) { + // found deleted slot, remember for later + if (avail_slot == NULL) { + avail_slot = &set->table[pos]; + } + } else if (mp_obj_equal(elem, index)) { + // found index + if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) { + // delete element + set->used--; + if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) { + // optimisation if next slot is empty + set->table[pos] = MP_OBJ_NULL; + } else { + set->table[pos] = MP_OBJ_SENTINEL; + } + } + return elem; + } + + // not yet found, keep searching in this table + pos = (pos + 1) % set->alloc; + + if (pos == start_pos) { + // search got back to starting position, so index is not in table + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot != NULL) { + // there was an available slot, so use that + set->used++; + *avail_slot = index; + return index; + } else { // not enough room in table, rehash it mp_set_rehash(set); // restart the search for the new element - pos = hash % set->alloc; - } else { - set->used += 1; - set->table[pos] = index; - return index; + start_pos = pos = hash % set->alloc; } - } else if (lookup_kind & MP_MAP_LOOKUP_FIRST) { - pos++; } else { return MP_OBJ_NULL; } - } else if ((lookup_kind & MP_MAP_LOOKUP_FIRST) || mp_obj_equal(elem, index)) { - // found it - if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) { - set->used--; - set->table[pos] = NULL; + } + } +} + +mp_obj_t mp_set_remove_first(mp_set_t *set) { + for (uint pos = 0; pos < set->alloc; pos++) { + if (set->table[pos] != MP_OBJ_NULL && set->table[pos] != MP_OBJ_SENTINEL) { + mp_obj_t elem = set->table[pos]; + // delete element + set->used--; + if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) { + // optimisation if next slot is empty + set->table[pos] = MP_OBJ_NULL; + } else { + set->table[pos] = MP_OBJ_SENTINEL; } return elem; - } else { - // not yet found, keep searching in this table - pos = (pos + 1) % set->alloc; } } + return MP_OBJ_NULL; } void mp_set_clear(mp_set_t *set) { |