blob: 696c7a2ea26d9fabe50f5ad2672838c6166fd298 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
Alexander Steffen55f33242017-06-30 09:22:17 +02002 * This file is part of the MicroPython project, http://micropython.org/
Damien George04b91472014-05-03 23:27:38 +01003 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Damien George93965e72014-08-30 13:23:35 +010027#include <stdint.h>
Damien660365e2013-12-17 18:27:24 +000028#include <stdlib.h>
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020029#include <string.h>
Damien George8b0535e2014-04-05 21:53:54 +010030#include <assert.h>
Damien660365e2013-12-17 18:27:24 +000031
Damien George51dfcb42015-01-01 20:27:54 +000032#include "py/mpconfig.h"
33#include "py/misc.h"
Damien Georgec2a4e4e2015-05-11 12:25:19 +000034#include "py/runtime.h"
Damien660365e2013-12-17 18:27:24 +000035
Paul Sokolovskya35d9232017-12-09 17:30:55 +020036#if MICROPY_DEBUG_VERBOSE // print debugging info
37#define DEBUG_PRINT (1)
38#else // don't print debugging info
39#define DEBUG_PRINT (0)
40#define DEBUG_printf(...) (void)0
41#endif
42
Paul Sokolovskye5dbe1e2014-11-26 21:17:16 +020043// Fixed empty map. Useful when need to call kw-receiving functions
44// without any keywords from C, etc.
45const mp_map_t mp_const_empty_map = {
46 .all_keys_are_qstrs = 0,
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020047 .is_fixed = 1,
48 .is_ordered = 1,
Paul Sokolovskye5dbe1e2014-11-26 21:17:16 +020049 .used = 0,
50 .alloc = 0,
51 .table = NULL,
52};
53
Damien George00137b82016-04-15 16:24:46 +010054// This table of sizes is used to control the growth of hash tables.
55// The first set of sizes are chosen so the allocation fits exactly in a
56// 4-word GC block, and it's not so important for these small values to be
57// prime. The latter sizes are prime and increase at an increasing rate.
Damien George3ff16ff2016-05-20 12:38:15 +010058STATIC const uint16_t hash_allocation_sizes[] = {
Damien George00137b82016-04-15 16:24:46 +010059 0, 2, 4, 6, 8, 10, 12, // +2
60 17, 23, 29, 37, 47, 59, 73, // *1.25
61 97, 127, 167, 223, 293, 389, 521, 691, 919, 1223, 1627, 2161, // *1.33
62 3229, 4831, 7243, 10861, 16273, 24407, 36607, 54907, // *1.5
63};
Damien660365e2013-12-17 18:27:24 +000064
Damien Georgeaf622eb2017-02-08 11:00:15 +110065STATIC size_t get_hash_alloc_greater_or_equal_to(size_t x) {
Damien George00137b82016-04-15 16:24:46 +010066 for (size_t i = 0; i < MP_ARRAY_SIZE(hash_allocation_sizes); i++) {
67 if (hash_allocation_sizes[i] >= x) {
68 return hash_allocation_sizes[i];
Damien660365e2013-12-17 18:27:24 +000069 }
70 }
71 // ran out of primes in the table!
72 // return something sensible, at least make it odd
Damien George00137b82016-04-15 16:24:46 +010073 return (x + x / 2) | 1;
Damien660365e2013-12-17 18:27:24 +000074}
75
Damiend99b0522013-12-21 18:17:45 +000076/******************************************************************************/
77/* map */
78
Damien Georgeaf622eb2017-02-08 11:00:15 +110079void mp_map_init(mp_map_t *map, size_t n) {
Damien George9a58d762014-02-08 18:47:46 +000080 if (n == 0) {
81 map->alloc = 0;
82 map->table = NULL;
83 } else {
Paul Sokolovsky5fedd0c2014-04-06 21:00:58 +030084 map->alloc = n;
Damien George9a58d762014-02-08 18:47:46 +000085 map->table = m_new0(mp_map_elem_t, map->alloc);
86 }
Damien George38a2da62014-01-08 17:33:12 +000087 map->used = 0;
88 map->all_keys_are_qstrs = 1;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020089 map->is_fixed = 0;
90 map->is_ordered = 0;
Damien George9a58d762014-02-08 18:47:46 +000091}
92
Damien Georgeaf622eb2017-02-08 11:00:15 +110093void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table) {
Damien George9a58d762014-02-08 18:47:46 +000094 map->alloc = n;
95 map->used = n;
96 map->all_keys_are_qstrs = 1;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020097 map->is_fixed = 1;
98 map->is_ordered = 1;
Damien George9a58d762014-02-08 18:47:46 +000099 map->table = (mp_map_elem_t*)table;
Damien660365e2013-12-17 18:27:24 +0000100}
101
Paul Sokolovsky9a24a042014-01-25 00:02:20 +0200102// Differentiate from mp_map_clear() - semantics is different
103void mp_map_deinit(mp_map_t *map) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200104 if (!map->is_fixed) {
Damien George9a58d762014-02-08 18:47:46 +0000105 m_del(mp_map_elem_t, map->table, map->alloc);
106 }
Paul Sokolovsky9a24a042014-01-25 00:02:20 +0200107 map->used = map->alloc = 0;
108}
109
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000110void mp_map_clear(mp_map_t *map) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200111 if (!map->is_fixed) {
Damien George9a58d762014-02-08 18:47:46 +0000112 m_del(mp_map_elem_t, map->table, map->alloc);
113 }
114 map->alloc = 0;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000115 map->used = 0;
Damien George38a2da62014-01-08 17:33:12 +0000116 map->all_keys_are_qstrs = 1;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200117 map->is_fixed = 0;
Damien George9a58d762014-02-08 18:47:46 +0000118 map->table = NULL;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000119}
120
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200121STATIC void mp_map_rehash(mp_map_t *map) {
Damien Georgeaf622eb2017-02-08 11:00:15 +1100122 size_t old_alloc = map->alloc;
123 size_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1);
Paul Sokolovskya35d9232017-12-09 17:30:55 +0200124 DEBUG_printf("mp_map_rehash(%p): " UINT_FMT " -> " UINT_FMT "\n", map, old_alloc, new_alloc);
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000125 mp_map_elem_t *old_table = map->table;
Stephen Kyleb4753272016-04-01 10:51:40 +0100126 mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc);
127 // If we reach this point, table resizing succeeded, now we can edit the old map.
128 map->alloc = new_alloc;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000129 map->used = 0;
Damien George38a2da62014-01-08 17:33:12 +0000130 map->all_keys_are_qstrs = 1;
Stephen Kyleb4753272016-04-01 10:51:40 +0100131 map->table = new_table;
Damien Georgeaf622eb2017-02-08 11:00:15 +1100132 for (size_t i = 0; i < old_alloc; i++) {
Damien George95004e52014-04-05 17:17:19 +0100133 if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) {
Damien George38a2da62014-01-08 17:33:12 +0000134 mp_map_lookup(map, old_table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_table[i].value;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000135 }
136 }
137 m_del(mp_map_elem_t, old_table, old_alloc);
138}
139
Damien Georged0e82432014-04-05 23:33:12 +0100140// MP_MAP_LOOKUP behaviour:
141// - returns NULL if not found, else the slot it was found in with key,value non-null
142// MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour:
143// - returns slot, with key non-null and value=MP_OBJ_NULL if it was added
144// MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour:
145// - returns NULL if not found, else the slot if was found in with key null and value non-null
Damien George2801e6f2015-04-04 15:53:11 +0100146mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
Damien George0e420d42017-08-31 16:45:02 +1000147 // If the map is a fixed array then we must only be called for a lookup
148 assert(!map->is_fixed || lookup_kind == MP_MAP_LOOKUP);
Damien George6dde0192015-12-31 00:19:28 +0000149
Damien George186e4632014-04-28 12:11:57 +0100150 // Work out if we can compare just pointers
151 bool compare_only_ptrs = map->all_keys_are_qstrs;
152 if (compare_only_ptrs) {
153 if (MP_OBJ_IS_QSTR(index)) {
154 // Index is a qstr, so can just do ptr comparison.
155 } else if (MP_OBJ_IS_TYPE(index, &mp_type_str)) {
156 // Index is a non-interned string.
157 // We can either intern the string, or force a full equality comparison.
158 // We chose the latter, since interning costs time and potentially RAM,
159 // and it won't necessarily benefit subsequent calls because these calls
160 // most likely won't pass the newly-interned string.
161 compare_only_ptrs = false;
Damien Georged1cee022015-03-20 17:41:37 +0000162 } else if (lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George186e4632014-04-28 12:11:57 +0100163 // If we are not adding, then we can return straight away a failed
164 // lookup because we know that the index will never be found.
165 return NULL;
166 }
167 }
168
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200169 // if the map is an ordered array then we must do a brute force linear search
170 if (map->is_ordered) {
Damien George9a58d762014-02-08 18:47:46 +0000171 for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
Damien George186e4632014-04-28 12:11:57 +0100172 if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200173 if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) {
Damien George9275c182017-03-03 11:21:19 +1100174 // remove the found element by moving the rest of the array down
175 mp_obj_t value = elem->value;
176 --map->used;
177 memmove(elem, elem + 1, (top - elem - 1) * sizeof(*elem));
178 // put the found element after the end so the caller can access it if needed
179 elem = &map->table[map->used];
180 elem->key = MP_OBJ_NULL;
181 elem->value = value;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200182 }
Damien George9a58d762014-02-08 18:47:46 +0000183 return elem;
184 }
185 }
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200186 if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) {
187 return NULL;
188 }
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200189 if (map->used == map->alloc) {
190 // TODO: Alloc policy
191 map->alloc += 4;
192 map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc);
193 mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table));
194 }
195 mp_map_elem_t *elem = map->table + map->used++;
196 elem->key = index;
197 if (!MP_OBJ_IS_QSTR(index)) {
198 map->all_keys_are_qstrs = 0;
199 }
200 return elem;
Damien George9a58d762014-02-08 18:47:46 +0000201 }
202
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200203 // map is a hash table (not an ordered array), so do a hash lookup
Damien George9a58d762014-02-08 18:47:46 +0000204
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000205 if (map->alloc == 0) {
Damien Georged1cee022015-03-20 17:41:37 +0000206 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000207 mp_map_rehash(map);
208 } else {
209 return NULL;
210 }
211 }
Damien George9a58d762014-02-08 18:47:46 +0000212
Damien Georgebbe8d512015-12-26 21:15:47 +0000213 // get hash of index, with fast path for common case of qstr
214 mp_uint_t hash;
215 if (MP_OBJ_IS_QSTR(index)) {
216 hash = qstr_hash(MP_OBJ_QSTR_VALUE(index));
217 } else {
218 hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
219 }
220
Damien Georgeaf622eb2017-02-08 11:00:15 +1100221 size_t pos = hash % map->alloc;
222 size_t start_pos = pos;
Damien George95004e52014-04-05 17:17:19 +0100223 mp_map_elem_t *avail_slot = NULL;
Damien660365e2013-12-17 18:27:24 +0000224 for (;;) {
Damien George95004e52014-04-05 17:17:19 +0100225 mp_map_elem_t *slot = &map->table[pos];
226 if (slot->key == MP_OBJ_NULL) {
227 // found NULL slot, so index is not in table
Damien Georged1cee022015-03-20 17:41:37 +0000228 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100229 map->used += 1;
230 if (avail_slot == NULL) {
231 avail_slot = slot;
Damien660365e2013-12-17 18:27:24 +0000232 }
Damien George593faf12015-11-19 01:27:28 +0000233 avail_slot->key = index;
234 avail_slot->value = MP_OBJ_NULL;
Damien George95004e52014-04-05 17:17:19 +0100235 if (!MP_OBJ_IS_QSTR(index)) {
236 map->all_keys_are_qstrs = 0;
237 }
Damien George593faf12015-11-19 01:27:28 +0000238 return avail_slot;
Damien George95004e52014-04-05 17:17:19 +0100239 } else {
Damien Georged0e82432014-04-05 23:33:12 +0100240 return NULL;
Damien660365e2013-12-17 18:27:24 +0000241 }
Damien George95004e52014-04-05 17:17:19 +0100242 } else if (slot->key == MP_OBJ_SENTINEL) {
243 // found deleted slot, remember for later
244 if (avail_slot == NULL) {
245 avail_slot = slot;
Damien660365e2013-12-17 18:27:24 +0000246 }
Damien George186e4632014-04-28 12:11:57 +0100247 } else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) {
Damien George95004e52014-04-05 17:17:19 +0100248 // found index
249 // Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x
Damien Georged1cee022015-03-20 17:41:37 +0000250 if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100251 // delete element in this slot
252 map->used--;
253 if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) {
254 // optimisation if next slot is empty
255 slot->key = MP_OBJ_NULL;
256 } else {
257 slot->key = MP_OBJ_SENTINEL;
258 }
Damien Georged0e82432014-04-05 23:33:12 +0100259 // keep slot->value so that caller can access it if needed
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000260 }
Damien George95004e52014-04-05 17:17:19 +0100261 return slot;
Damien660365e2013-12-17 18:27:24 +0000262 }
Paul Sokolovsky4a088f42014-04-05 04:17:17 +0300263
264 // not yet found, keep searching in this table
265 pos = (pos + 1) % map->alloc;
Damien George95004e52014-04-05 17:17:19 +0100266
267 if (pos == start_pos) {
268 // search got back to starting position, so index is not in table
Damien Georged1cee022015-03-20 17:41:37 +0000269 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100270 if (avail_slot != NULL) {
271 // there was an available slot, so use that
272 map->used++;
273 avail_slot->key = index;
274 avail_slot->value = MP_OBJ_NULL;
275 if (!MP_OBJ_IS_QSTR(index)) {
276 map->all_keys_are_qstrs = 0;
277 }
278 return avail_slot;
279 } else {
280 // not enough room in table, rehash it
281 mp_map_rehash(map);
282 // restart the search for the new element
283 start_pos = pos = hash % map->alloc;
284 }
285 } else {
Damien Georged0e82432014-04-05 23:33:12 +0100286 return NULL;
Damien George95004e52014-04-05 17:17:19 +0100287 }
288 }
Damien660365e2013-12-17 18:27:24 +0000289 }
290}
291
Damiend99b0522013-12-21 18:17:45 +0000292/******************************************************************************/
293/* set */
294
Damien Georgee37dcaa2014-12-27 17:07:16 +0000295#if MICROPY_PY_BUILTINS_SET
296
Damien Georgeaf622eb2017-02-08 11:00:15 +1100297void mp_set_init(mp_set_t *set, size_t n) {
Damien George2bfd2dc2014-04-07 01:16:17 +0100298 set->alloc = n;
Damiend99b0522013-12-21 18:17:45 +0000299 set->used = 0;
300 set->table = m_new0(mp_obj_t, set->alloc);
Damien660365e2013-12-17 18:27:24 +0000301}
302
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200303STATIC void mp_set_rehash(mp_set_t *set) {
Damien Georgeaf622eb2017-02-08 11:00:15 +1100304 size_t old_alloc = set->alloc;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000305 mp_obj_t *old_table = set->table;
Damien George00137b82016-04-15 16:24:46 +0100306 set->alloc = get_hash_alloc_greater_or_equal_to(set->alloc + 1);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000307 set->used = 0;
308 set->table = m_new0(mp_obj_t, set->alloc);
Damien Georgeaf622eb2017-02-08 11:00:15 +1100309 for (size_t i = 0; i < old_alloc; i++) {
Damien George95004e52014-04-05 17:17:19 +0100310 if (old_table[i] != MP_OBJ_NULL && old_table[i] != MP_OBJ_SENTINEL) {
311 mp_set_lookup(set, old_table[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000312 }
313 }
314 m_del(mp_obj_t, old_table, old_alloc);
315}
316
John R. Lenton2a241722014-01-12 16:39:39 +0000317mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
Damien Georged1cee022015-03-20 17:41:37 +0000318 // Note: lookup_kind can be MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND which
319 // is handled by using bitwise operations.
320
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000321 if (set->alloc == 0) {
John R. Lentonae00d332014-01-12 18:23:36 +0000322 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000323 mp_set_rehash(set);
324 } else {
Damien George83229d32015-11-20 14:09:20 +0000325 return MP_OBJ_NULL;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000326 }
327 }
Damien Georgec2a4e4e2015-05-11 12:25:19 +0000328 mp_uint_t hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
Damien Georgeaf622eb2017-02-08 11:00:15 +1100329 size_t pos = hash % set->alloc;
330 size_t start_pos = pos;
Damien George95004e52014-04-05 17:17:19 +0100331 mp_obj_t *avail_slot = NULL;
Damien660365e2013-12-17 18:27:24 +0000332 for (;;) {
Damiend99b0522013-12-21 18:17:45 +0000333 mp_obj_t elem = set->table[pos];
334 if (elem == MP_OBJ_NULL) {
Damien George95004e52014-04-05 17:17:19 +0100335 // found NULL slot, so index is not in table
John R. Lentonae00d332014-01-12 18:23:36 +0000336 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100337 if (avail_slot == NULL) {
338 avail_slot = &set->table[pos];
Damien660365e2013-12-17 18:27:24 +0000339 }
Damien George95004e52014-04-05 17:17:19 +0100340 set->used++;
341 *avail_slot = index;
342 return index;
Damien660365e2013-12-17 18:27:24 +0000343 } else {
Damiend99b0522013-12-21 18:17:45 +0000344 return MP_OBJ_NULL;
Damien660365e2013-12-17 18:27:24 +0000345 }
Damien George95004e52014-04-05 17:17:19 +0100346 } else if (elem == MP_OBJ_SENTINEL) {
347 // found deleted slot, remember for later
348 if (avail_slot == NULL) {
349 avail_slot = &set->table[pos];
350 }
351 } else if (mp_obj_equal(elem, index)) {
352 // found index
John R. Lentonae00d332014-01-12 18:23:36 +0000353 if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100354 // delete element
John R. Lenton2a241722014-01-12 16:39:39 +0000355 set->used--;
Damien George95004e52014-04-05 17:17:19 +0100356 if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
357 // optimisation if next slot is empty
358 set->table[pos] = MP_OBJ_NULL;
359 } else {
360 set->table[pos] = MP_OBJ_SENTINEL;
361 }
John R. Lenton2a241722014-01-12 16:39:39 +0000362 }
Damien660365e2013-12-17 18:27:24 +0000363 return elem;
Damien George95004e52014-04-05 17:17:19 +0100364 }
365
366 // not yet found, keep searching in this table
367 pos = (pos + 1) % set->alloc;
368
369 if (pos == start_pos) {
370 // search got back to starting position, so index is not in table
371 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
372 if (avail_slot != NULL) {
373 // there was an available slot, so use that
374 set->used++;
375 *avail_slot = index;
376 return index;
377 } else {
378 // not enough room in table, rehash it
379 mp_set_rehash(set);
380 // restart the search for the new element
381 start_pos = pos = hash % set->alloc;
382 }
383 } else {
384 return MP_OBJ_NULL;
385 }
Damien660365e2013-12-17 18:27:24 +0000386 }
387 }
388}
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000389
Damien George95004e52014-04-05 17:17:19 +0100390mp_obj_t mp_set_remove_first(mp_set_t *set) {
Damien Georgeaf622eb2017-02-08 11:00:15 +1100391 for (size_t pos = 0; pos < set->alloc; pos++) {
Damien George8b0535e2014-04-05 21:53:54 +0100392 if (MP_SET_SLOT_IS_FILLED(set, pos)) {
Damien George95004e52014-04-05 17:17:19 +0100393 mp_obj_t elem = set->table[pos];
394 // delete element
395 set->used--;
396 if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
397 // optimisation if next slot is empty
398 set->table[pos] = MP_OBJ_NULL;
399 } else {
400 set->table[pos] = MP_OBJ_SENTINEL;
401 }
402 return elem;
403 }
404 }
405 return MP_OBJ_NULL;
406}
407
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000408void mp_set_clear(mp_set_t *set) {
Damien George9a58d762014-02-08 18:47:46 +0000409 m_del(mp_obj_t, set->table, set->alloc);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000410 set->alloc = 0;
Damien George9a58d762014-02-08 18:47:46 +0000411 set->used = 0;
412 set->table = NULL;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000413}
Paul Sokolovskye3f58c82014-04-05 04:14:22 +0300414
Damien Georgee37dcaa2014-12-27 17:07:16 +0000415#endif // MICROPY_PY_BUILTINS_SET
416
Damien George7860c2a2014-11-05 21:16:41 +0000417#if defined(DEBUG_PRINT) && DEBUG_PRINT
Paul Sokolovskye3f58c82014-04-05 04:14:22 +0300418void mp_map_dump(mp_map_t *map) {
Damien Georgeaf622eb2017-02-08 11:00:15 +1100419 for (size_t i = 0; i < map->alloc; i++) {
Paul Sokolovskye3f58c82014-04-05 04:14:22 +0300420 if (map->table[i].key != NULL) {
421 mp_obj_print(map->table[i].key, PRINT_REPR);
422 } else {
423 printf("(nil)");
424 }
425 printf(": %p\n", map->table[i].value);
426 }
427 printf("---\n");
428}
429#endif