STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_dict_t *o = MP_OBJ_TO_PTR(lhs_in); switch (op) { case MP_BINARY_OP_CONTAINS: { mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP); return mp_obj_new_bool(elem != NULL); } case MP_BINARY_OP_EQUAL: { #if MICROPY_PY_COLLECTIONS_ORDEREDDICT if (MP_UNLIKELY(mp_obj_is_type(lhs_in, &mp_type_ordereddict) && mp_obj_is_type(rhs_in, &mp_type_ordereddict))) { // Iterate through both dictionaries simultaneously and compare keys and values. mp_obj_dict_t *rhs = MP_OBJ_TO_PTR(rhs_in); size_t c1 = 0, c2 = 0; mp_map_elem_t *e1 = dict_iter_next(o, &c1), *e2 = dict_iter_next(rhs, &c2); for (; e1 != NULL && e2 != NULL; e1 = dict_iter_next(o, &c1), e2 = dict_iter_next(rhs, &c2)) { if (!mp_obj_equal(e1->key, e2->key) || !mp_obj_equal(e1->value, e2->value)) { return mp_const_false; } } return e1 == NULL && e2 == NULL ? mp_const_true : mp_const_false; } else #endif if (mp_obj_is_type(rhs_in, &mp_type_dict)) { mp_obj_dict_t *rhs = MP_OBJ_TO_PTR(rhs_in); if (o->map.used != rhs->map.used) { return mp_const_false; } size_t cur = 0; mp_map_elem_t *next = NULL; while ((next = dict_iter_next(o, &cur)) != NULL) { mp_map_elem_t *elem = mp_map_lookup(&rhs->map, next->key, MP_MAP_LOOKUP); if (elem == NULL || !mp_obj_equal(next->value, elem->value)) { return mp_const_false; } } return mp_const_true; } else { // dict is not equal to instance of any other type return mp_const_false; } } default: // op not supported return MP_OBJ_NULL; } }
mp_obj_t mp_seq_count_obj(const mp_obj_t *items, size_t len, mp_obj_t value) { size_t count = 0; for (size_t i = 0; i < len; i++) { if (mp_obj_equal(items[i], value)) { count++; } } // Common sense says this cannot overflow small int return MP_OBJ_NEW_SMALL_INT(count); }
// Don't pass RT_COMPARE_OP_NOT_EQUAL here static bool list_cmp_helper(int op, mp_obj_t self_in, mp_obj_t another_in) { assert(MP_OBJ_IS_TYPE(self_in, &list_type)); if (!MP_OBJ_IS_TYPE(another_in, &list_type)) { return false; } mp_obj_list_t *self = self_in; mp_obj_list_t *another = another_in; if (op == RT_COMPARE_OP_EQUAL && self->len != another->len) { return false; } // Let's deal only with > & >= if (op == RT_COMPARE_OP_LESS || op == RT_COMPARE_OP_LESS_EQUAL) { mp_obj_t t = self; self = another; another = t; if (op == RT_COMPARE_OP_LESS) { op = RT_COMPARE_OP_MORE; } else { op = RT_COMPARE_OP_MORE_EQUAL; } } int len = self->len < another->len ? self->len : another->len; bool eq_status = true; // empty lists are equal bool rel_status; for (int i = 0; i < len; i++) { eq_status = mp_obj_equal(self->items[i], another->items[i]); if (op == RT_COMPARE_OP_EQUAL && !eq_status) { return false; } rel_status = (rt_binary_op(op, self->items[i], another->items[i]) == mp_const_true); if (!eq_status && !rel_status) { return false; } } // If we had tie in the last element... if (eq_status) { // ... and we have lists of different lengths... if (self->len != another->len) { if (self->len < another->len) { // ... then longer list length wins (we deal only with >) return false; } } else if (op == RT_COMPARE_OP_MORE) { // Otherwise, if we have strict relation, equality means failure return false; } } return true; }
static mp_obj_t list_count(mp_obj_t self_in, mp_obj_t value) { assert(MP_OBJ_IS_TYPE(self_in, &list_type)); mp_obj_list_t *self = self_in; int count = 0; for (int i = 0; i < self->len; i++) { if (mp_obj_equal(self->items[i], value)) { count++; } } return mp_obj_new_int(count); }
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); } else { return NULL; } } if (lookup_kind & MP_MAP_LOOKUP_FIRST) { hash = 0; pos = 0; } else { hash = mp_obj_hash(index);; pos = hash % set->alloc; } for (;;) { mp_obj_t elem = set->table[pos]; if (elem == MP_OBJ_NULL) { // not in table if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { if (set->used + 1 >= set->alloc) { // 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; } } 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; } return elem; } else { // not yet found, keep searching in this table pos = (pos + 1) % set->alloc; } } }
// Special-case comparison function for sequences of mp_obj_t // Don't pass MP_BINARY_OP_NOT_EQUAL here bool mp_seq_cmp_objs(mp_uint_t op, const mp_obj_t *items1, size_t len1, const mp_obj_t *items2, size_t len2) { if (op == MP_BINARY_OP_EQUAL && len1 != len2) { return false; } // Let's deal only with > & >= if (op == MP_BINARY_OP_LESS || op == MP_BINARY_OP_LESS_EQUAL) { SWAP(const mp_obj_t *, items1, items2); SWAP(size_t, len1, len2); if (op == MP_BINARY_OP_LESS) { op = MP_BINARY_OP_MORE; } else { op = MP_BINARY_OP_MORE_EQUAL; } } size_t len = len1 < len2 ? len1 : len2; for (size_t i = 0; i < len; i++) { // If current elements equal, can't decide anything - go on if (mp_obj_equal(items1[i], items2[i])) { continue; } // Othewise, if they are not equal, we can have final decision based on them if (op == MP_BINARY_OP_EQUAL) { // In particular, if we are checking for equality, here're the answer return false; } // Otherwise, application of relation op gives the answer return (mp_binary_op(op, items1[i], items2[i]) == mp_const_true); } // If we had tie in the last element... // ... and we have lists of different lengths... if (len1 != len2) { if (len1 < len2) { // ... then longer list length wins (we deal only with >) return false; } } else if (op == MP_BINARY_OP_MORE) { // Otherwise, if we have strict relation, sequence equality means failure return false; } return true; }
STATIC mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_dict_t *o = lhs_in; switch (op) { case MP_BINARY_OP_SUBSCR: { // dict load mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP); if (elem == NULL) { nlr_raise(mp_obj_new_exception_msg(&mp_type_KeyError, "<value>")); } else { return elem->value; } } case MP_BINARY_OP_IN: { mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP); return MP_BOOL(elem != NULL); } case MP_BINARY_OP_EQUAL: { if (MP_OBJ_IS_TYPE(rhs_in, &mp_type_dict)) { mp_obj_dict_t *rhs = rhs_in; if (o->map.used != rhs->map.used) { return mp_const_false; } machine_uint_t size = o->map.alloc; mp_map_t *map = &o->map; for (machine_uint_t i = 0; i < size; i++) { if (MP_MAP_SLOT_IS_FILLED(map, i)) { mp_map_elem_t *elem = mp_map_lookup(&rhs->map, map->table[i].key, MP_MAP_LOOKUP); if (elem == NULL || !mp_obj_equal(map->table[i].value, elem->value)) { return mp_const_false; } } } return mp_const_true; } else { // dict is not equal to instance of any other type return mp_const_false; } } default: // op not supported return NULL; } }
// Special-case comparison function for sequences of mp_obj_t // Don't pass RT_BINARY_OP_NOT_EQUAL here bool mp_seq_cmp_objs(int op, const mp_obj_t *items1, uint len1, const mp_obj_t *items2, uint len2) { if (op == RT_BINARY_OP_EQUAL && len1 != len2) { return false; } // Let's deal only with > & >= if (op == RT_BINARY_OP_LESS || op == RT_BINARY_OP_LESS_EQUAL) { SWAP(const mp_obj_t *, items1, items2); SWAP(uint, len1, len2); if (op == RT_BINARY_OP_LESS) { op = RT_BINARY_OP_MORE; } else { op = RT_BINARY_OP_MORE_EQUAL; } } int len = len1 < len2 ? len1 : len2; bool eq_status = true; // empty lists are equal bool rel_status; for (int i = 0; i < len; i++) { eq_status = mp_obj_equal(items1[i], items2[i]); if (op == RT_BINARY_OP_EQUAL && !eq_status) { return false; } rel_status = (rt_binary_op(op, items1[i], items2[i]) == mp_const_true); if (!eq_status && !rel_status) { return false; } } // If we had tie in the last element... if (eq_status) { // ... and we have lists of different lengths... if (len1 != len2) { if (len1 < len2) { // ... then longer list length wins (we deal only with >) return false; } } else if (op == RT_BINARY_OP_MORE) { // Otherwise, if we have strict relation, equality means failure return false; } } return true; }
// Special-case of index() which searches for mp_obj_t mp_obj_t mp_seq_index_obj(const mp_obj_t *items, size_t len, size_t n_args, const mp_obj_t *args) { mp_obj_type_t *type = mp_obj_get_type(args[0]); mp_obj_t value = args[1]; size_t start = 0; size_t stop = len; if (n_args >= 3) { start = mp_get_index(type, len, args[2], true); if (n_args >= 4) { stop = mp_get_index(type, len, args[3], true); } } for (size_t i = start; i < stop; i++) { if (mp_obj_equal(items[i], value)) { // Common sense says this cannot overflow small int return MP_OBJ_NEW_SMALL_INT(i); } } mp_raise_ValueError("object not in sequence"); }
// Special-case of index() which searches for mp_obj_t mp_obj_t mp_seq_index_obj(const mp_obj_t *items, uint len, uint n_args, const mp_obj_t *args) { mp_obj_type_t *type = mp_obj_get_type(args[0]); mp_obj_t *value = args[1]; uint start = 0; uint stop = len; if (n_args >= 3) { start = mp_get_index(type, len, args[2]); if (n_args >= 4) { stop = mp_get_index(type, len, args[3]); } } for (machine_uint_t i = start; i < stop; i++) { if (mp_obj_equal(items[i], value)) { // Common sense says this cannot overflow small int return MP_OBJ_NEW_SMALL_INT(i); } } nlr_jump(mp_obj_new_exception_msg(MP_QSTR_ValueError, "object not in sequence")); }
STATIC mp_obj_t dict_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_dict_t *o = MP_OBJ_CAST(lhs_in); switch (op) { case MP_BINARY_OP_IN: { mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP); return MP_BOOL(elem != NULL); } case MP_BINARY_OP_EQUAL: { #if MICROPY_PY_COLLECTIONS_ORDEREDDICT if (MP_UNLIKELY(MP_OBJ_IS_TYPE(lhs_in, &mp_type_ordereddict) && MP_OBJ_IS_TYPE(rhs_in, &mp_type_ordereddict))) { //TODO: implement return MP_OBJ_NULL; } else #endif if (MP_OBJ_IS_TYPE(rhs_in, &mp_type_dict)) { mp_obj_dict_t *rhs = MP_OBJ_CAST(rhs_in); if (o->map.used != rhs->map.used) { return mp_const_false; } mp_uint_t cur = 0; mp_map_elem_t *next = NULL; while ((next = dict_iter_next(o, &cur)) != NULL) { mp_map_elem_t *elem = mp_map_lookup(&rhs->map, next->key, MP_MAP_LOOKUP); if (elem == NULL || !mp_obj_equal(next->value, elem->value)) { return mp_const_false; } } return mp_const_true; } else { // dict is not equal to instance of any other type return mp_const_false; } } default: // op not supported return MP_OBJ_NULL; } }
static mp_obj_t list_index(uint n_args, const mp_obj_t *args) { assert(2 <= n_args && n_args <= 4); assert(MP_OBJ_IS_TYPE(args[0], &list_type)); mp_obj_list_t *self = args[0]; mp_obj_t *value = args[1]; uint start = 0; uint stop = self->len; if (n_args >= 3) { start = mp_get_index(self->base.type, self->len, args[2]); if (n_args >= 4) { stop = mp_get_index(self->base.type, self->len, args[3]); } } for (uint i = start; i < stop; i++) { if (mp_obj_equal(self->items[i], value)) { return mp_obj_new_int(i); } } nlr_jump(mp_obj_new_exception_msg(MP_QSTR_ValueError, "object not in list")); }
mp_map_elem_t* mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { // if the map is a fixed array then we must do a brute force linear search if (map->table_is_fixed_array) { if (lookup_kind != MP_MAP_LOOKUP) { return NULL; } for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) { if (elem->key == index || (!map->all_keys_are_qstrs && mp_obj_equal(elem->key, index))) { return elem; } } return NULL; } // 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); } else { return NULL; } } uint pos = hash % map->alloc; for (;;) { mp_map_elem_t *elem = &map->table[pos]; if (elem->key == NULL) { // 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; } } else { return 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; } */ 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; return retval; } return elem; } else { // not yet found, keep searching in this table pos = (pos + 1) % map->alloc; } } }
mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { // Note: lookup_kind can be MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND which // is handled by using bitwise operations. if (set->alloc == 0) { if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_set_rehash(set); } else { return MP_OBJ_NULL; } } mp_uint_t hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index)); size_t pos = hash % set->alloc; size_t start_pos = pos; mp_obj_t *avail_slot = NULL; for (;;) { mp_obj_t elem = set->table[pos]; if (elem == MP_OBJ_NULL) { // found NULL slot, so index is not in table if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { 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 start_pos = pos = hash % set->alloc; } } else { return MP_OBJ_NULL; } } } }
// MP_MAP_LOOKUP behaviour: // - returns NULL if not found, else the slot it was found in with key,value non-null // MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour: // - returns slot, with key non-null and value=MP_OBJ_NULL if it was added // MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour: // - returns NULL if not found, else the slot if was found in with key null and value non-null mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { if (map->is_fixed && lookup_kind != MP_MAP_LOOKUP) { // can't add/remove from a fixed array return NULL; } // Work out if we can compare just pointers bool compare_only_ptrs = map->all_keys_are_qstrs; if (compare_only_ptrs) { if (MP_OBJ_IS_QSTR(index)) { // Index is a qstr, so can just do ptr comparison. } else if (MP_OBJ_IS_TYPE(index, &mp_type_str)) { // Index is a non-interned string. // We can either intern the string, or force a full equality comparison. // We chose the latter, since interning costs time and potentially RAM, // and it won't necessarily benefit subsequent calls because these calls // most likely won't pass the newly-interned string. compare_only_ptrs = false; } else if (lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { // If we are not adding, then we can return straight away a failed // lookup because we know that the index will never be found. return NULL; } } // if the map is an ordered array then we must do a brute force linear search if (map->is_ordered) { for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) { if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) { if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) { // remove the found element by moving the rest of the array down mp_obj_t value = elem->value; --map->used; memmove(elem, elem + 1, (top - elem - 1) * sizeof(*elem)); // put the found element after the end so the caller can access it if needed elem = &map->table[map->used]; elem->key = MP_OBJ_NULL; elem->value = value; } return elem; } } if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) { return NULL; } if (map->used == map->alloc) { // TODO: Alloc policy map->alloc += 4; map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc); mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table)); } mp_map_elem_t *elem = map->table + map->used++; elem->key = index; if (!MP_OBJ_IS_QSTR(index)) { map->all_keys_are_qstrs = 0; } return elem; } // map is a hash table (not an ordered array), so do a hash lookup if (map->alloc == 0) { if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_map_rehash(map); } else { return NULL; } } // get hash of index, with fast path for common case of qstr mp_uint_t hash; if (MP_OBJ_IS_QSTR(index)) { hash = qstr_hash(MP_OBJ_QSTR_VALUE(index)); } else { hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index)); } size_t pos = hash % map->alloc; size_t start_pos = pos; mp_map_elem_t *avail_slot = NULL; for (;;) { 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) { map->used += 1; if (avail_slot == NULL) { avail_slot = slot; } 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 { return NULL; } } 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 || (!compare_only_ptrs && 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) { // 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; } // keep slot->value so that caller can access it if needed } 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 NULL; } } } }
// MP_MAP_LOOKUP behaviour: // - returns NULL if not found, else the slot it was found in with key,value non-null // MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour: // - returns slot, with key non-null and value=MP_OBJ_NULL if it was added // MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour: // - returns NULL if not found, else the slot if was found in with key null and value non-null mp_map_elem_t* mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { // Work out if we can compare just pointers bool compare_only_ptrs = map->all_keys_are_qstrs; if (compare_only_ptrs) { if (MP_OBJ_IS_QSTR(index)) { // Index is a qstr, so can just do ptr comparison. } else if (MP_OBJ_IS_TYPE(index, &mp_type_str)) { // Index is a non-interned string. // We can either intern the string, or force a full equality comparison. // We chose the latter, since interning costs time and potentially RAM, // and it won't necessarily benefit subsequent calls because these calls // most likely won't pass the newly-interned string. compare_only_ptrs = false; } else if (!(lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) { // If we are not adding, then we can return straight away a failed // lookup because we know that the index will never be found. return NULL; } } // if the map is a fixed array then we must do a brute force linear search if (map->table_is_fixed_array) { if (lookup_kind != MP_MAP_LOOKUP) { return NULL; } for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) { if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) { return elem; } } return NULL; } // map is a hash table (not a fixed array), so do a hash lookup if (map->alloc == 0) { if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_map_rehash(map); } else { return NULL; } } mp_uint_t hash = mp_obj_hash(index); mp_uint_t pos = hash % map->alloc; mp_uint_t start_pos = pos; mp_map_elem_t *avail_slot = NULL; for (;;) { 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) { 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; } } 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 || (!compare_only_ptrs && 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) { // 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; } // keep slot->value so that caller can access it if needed } 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 NULL; } } } }