Skip to content

Commit fcdac17

Browse files
committed
py/map: Add board option MICROPY_PY_MAP_ORDERED to make all dicts/maps ordered.
1 parent 1662a0b commit fcdac17

File tree

1 file changed

+114
-103
lines changed

1 file changed

+114
-103
lines changed

py/map.c

Lines changed: 114 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,19 @@ void mp_map_init(mp_map_t *map, size_t n) {
8787
map->used = 0;
8888
map->all_keys_are_qstrs = 1;
8989
map->is_fixed = 0;
90+
#if !MICROPY_PY_MAP_ORDERED
9091
map->is_ordered = 0;
92+
#endif
9193
}
9294

9395
void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table) {
9496
map->alloc = n;
9597
map->used = n;
9698
map->all_keys_are_qstrs = 1;
9799
map->is_fixed = 1;
100+
#if !MICROPY_PY_MAP_ORDERED
98101
map->is_ordered = 1;
102+
#endif
99103
map->table = (mp_map_elem_t *)table;
100104
}
101105

@@ -118,6 +122,7 @@ void mp_map_clear(mp_map_t *map) {
118122
map->table = NULL;
119123
}
120124

125+
#if !MICROPY_PY_MAP_ORDERED
121126
STATIC void mp_map_rehash(mp_map_t *map) {
122127
size_t old_alloc = map->alloc;
123128
size_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1);
@@ -136,6 +141,7 @@ STATIC void mp_map_rehash(mp_map_t *map) {
136141
}
137142
m_del(mp_map_elem_t, old_table, old_alloc);
138143
}
144+
#endif
139145

140146
// MP_MAP_LOOKUP behaviour:
141147
// - returns NULL if not found, else the slot it was found in with key,value non-null
@@ -167,134 +173,139 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t
167173
}
168174

169175
// if the map is an ordered array then we must do a brute force linear search
176+
#if !MICROPY_PY_MAP_ORDERED
170177
if (map->is_ordered) {
171-
for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
172-
if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) {
173-
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
174-
if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) {
175-
// remove the found element by moving the rest of the array down
176-
mp_obj_t value = elem->value;
177-
--map->used;
178-
memmove(elem, elem + 1, (top - elem - 1) * sizeof(*elem));
179-
// put the found element after the end so the caller can access it if needed
180-
// note: caller must NULL the value so the GC can clean up (e.g. see dict_get_helper).
181-
elem = &map->table[map->used];
182-
elem->key = MP_OBJ_NULL;
183-
elem->value = value;
184-
}
185-
#endif
186-
return elem;
178+
#endif
179+
for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
180+
if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) {
181+
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
182+
if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) {
183+
// remove the found element by moving the rest of the array down
184+
mp_obj_t value = elem->value;
185+
--map->used;
186+
memmove(elem, elem + 1, (top - elem - 1) * sizeof(*elem));
187+
// put the found element after the end so the caller can access it if needed
188+
// note: caller must NULL the value so the GC can clean up (e.g. see dict_get_helper).
189+
elem = &map->table[map->used];
190+
elem->key = MP_OBJ_NULL;
191+
elem->value = value;
187192
}
193+
#endif
194+
return elem;
188195
}
189-
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
190-
if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) {
191-
return NULL;
192-
}
193-
if (map->used == map->alloc) {
194-
// TODO: Alloc policy
195-
map->alloc += 4;
196-
map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc);
197-
mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table));
198-
}
199-
mp_map_elem_t *elem = map->table + map->used++;
200-
elem->key = index;
201-
if (!mp_obj_is_qstr(index)) {
202-
map->all_keys_are_qstrs = 0;
203-
}
204-
return elem;
205-
#else
196+
}
197+
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
198+
if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) {
206199
return NULL;
207-
#endif
208200
}
201+
if (map->used == map->alloc) {
202+
// TODO: Alloc policy
203+
map->alloc += 4;
204+
map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc);
205+
mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table));
206+
}
207+
mp_map_elem_t *elem = map->table + map->used++;
208+
elem->key = index;
209+
if (!mp_obj_is_qstr(index)) {
210+
map->all_keys_are_qstrs = 0;
211+
}
212+
return elem;
213+
#else
214+
return NULL;
215+
#endif
216+
217+
#if !MICROPY_PY_MAP_ORDERED
218+
}
209219

210-
// map is a hash table (not an ordered array), so do a hash lookup
220+
// map is a hash table (not an ordered array), so do a hash lookup
211221

212-
if (map->alloc == 0) {
222+
if (map->alloc == 0) {
223+
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
224+
mp_map_rehash(map);
225+
} else {
226+
return NULL;
227+
}
228+
}
229+
230+
// get hash of index, with fast path for common case of qstr
231+
mp_uint_t hash;
232+
if (mp_obj_is_qstr(index)) {
233+
hash = qstr_hash(MP_OBJ_QSTR_VALUE(index));
234+
} else {
235+
hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
236+
}
237+
238+
size_t pos = hash % map->alloc;
239+
size_t start_pos = pos;
240+
mp_map_elem_t *avail_slot = NULL;
241+
for (;;) {
242+
mp_map_elem_t *slot = &map->table[pos];
243+
if (slot->key == MP_OBJ_NULL) {
244+
// found NULL slot, so index is not in table
213245
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
214-
mp_map_rehash(map);
246+
map->used += 1;
247+
if (avail_slot == NULL) {
248+
avail_slot = slot;
249+
}
250+
avail_slot->key = index;
251+
avail_slot->value = MP_OBJ_NULL;
252+
if (!mp_obj_is_qstr(index)) {
253+
map->all_keys_are_qstrs = 0;
254+
}
255+
return avail_slot;
215256
} else {
216257
return NULL;
217258
}
259+
} else if (slot->key == MP_OBJ_SENTINEL) {
260+
// found deleted slot, remember for later
261+
if (avail_slot == NULL) {
262+
avail_slot = slot;
263+
}
264+
} else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) {
265+
// found index
266+
// Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x
267+
if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
268+
// delete element in this slot
269+
map->used--;
270+
if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) {
271+
// optimisation if next slot is empty
272+
slot->key = MP_OBJ_NULL;
273+
} else {
274+
slot->key = MP_OBJ_SENTINEL;
275+
}
276+
// keep slot->value so that caller can access it if needed
277+
}
278+
return slot;
218279
}
219280

220-
// get hash of index, with fast path for common case of qstr
221-
mp_uint_t hash;
222-
if (mp_obj_is_qstr(index)) {
223-
hash = qstr_hash(MP_OBJ_QSTR_VALUE(index));
224-
} else {
225-
hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
226-
}
281+
// not yet found, keep searching in this table
282+
pos = (pos + 1) % map->alloc;
227283

228-
size_t pos = hash % map->alloc;
229-
size_t start_pos = pos;
230-
mp_map_elem_t *avail_slot = NULL;
231-
for (;;) {
232-
mp_map_elem_t *slot = &map->table[pos];
233-
if (slot->key == MP_OBJ_NULL) {
234-
// found NULL slot, so index is not in table
235-
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
236-
map->used += 1;
237-
if (avail_slot == NULL) {
238-
avail_slot = slot;
239-
}
284+
if (pos == start_pos) {
285+
// search got back to starting position, so index is not in table
286+
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
287+
if (avail_slot != NULL) {
288+
// there was an available slot, so use that
289+
map->used++;
240290
avail_slot->key = index;
241291
avail_slot->value = MP_OBJ_NULL;
242292
if (!mp_obj_is_qstr(index)) {
243293
map->all_keys_are_qstrs = 0;
244294
}
245295
return avail_slot;
246296
} else {
247-
return NULL;
248-
}
249-
} else if (slot->key == MP_OBJ_SENTINEL) {
250-
// found deleted slot, remember for later
251-
if (avail_slot == NULL) {
252-
avail_slot = slot;
253-
}
254-
} else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) {
255-
// found index
256-
// Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x
257-
if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
258-
// delete element in this slot
259-
map->used--;
260-
if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) {
261-
// optimisation if next slot is empty
262-
slot->key = MP_OBJ_NULL;
263-
} else {
264-
slot->key = MP_OBJ_SENTINEL;
265-
}
266-
// keep slot->value so that caller can access it if needed
267-
}
268-
return slot;
269-
}
270-
271-
// not yet found, keep searching in this table
272-
pos = (pos + 1) % map->alloc;
273-
274-
if (pos == start_pos) {
275-
// search got back to starting position, so index is not in table
276-
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
277-
if (avail_slot != NULL) {
278-
// there was an available slot, so use that
279-
map->used++;
280-
avail_slot->key = index;
281-
avail_slot->value = MP_OBJ_NULL;
282-
if (!mp_obj_is_qstr(index)) {
283-
map->all_keys_are_qstrs = 0;
284-
}
285-
return avail_slot;
286-
} else {
287-
// not enough room in table, rehash it
288-
mp_map_rehash(map);
289-
// restart the search for the new element
290-
start_pos = pos = hash % map->alloc;
291-
}
292-
} else {
293-
return NULL;
297+
// not enough room in table, rehash it
298+
mp_map_rehash(map);
299+
// restart the search for the new element
300+
start_pos = pos = hash % map->alloc;
294301
}
302+
} else {
303+
return NULL;
295304
}
296305
}
297306
}
307+
#endif // !MICROPY_PY_MAP_ORDERED
308+
}
298309

299310
/******************************************************************************/
300311
/* set */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy