blob: 0916ec522df25d658e06443edcc5fbc1cf23330c [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
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/runtime0.h"
35#include "py/runtime.h"
Damien660365e2013-12-17 18:27:24 +000036
Paul Sokolovskye5dbe1e2014-11-26 21:17:16 +020037// Fixed empty map. Useful when need to call kw-receiving functions
38// without any keywords from C, etc.
39const mp_map_t mp_const_empty_map = {
40 .all_keys_are_qstrs = 0,
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020041 .is_fixed = 1,
42 .is_ordered = 1,
Paul Sokolovskye5dbe1e2014-11-26 21:17:16 +020043 .used = 0,
44 .alloc = 0,
45 .table = NULL,
46};
47
Damien George00137b82016-04-15 16:24:46 +010048// This table of sizes is used to control the growth of hash tables.
49// The first set of sizes are chosen so the allocation fits exactly in a
50// 4-word GC block, and it's not so important for these small values to be
51// prime. The latter sizes are prime and increase at an increasing rate.
Damien George3ff16ff2016-05-20 12:38:15 +010052STATIC const uint16_t hash_allocation_sizes[] = {
Damien George00137b82016-04-15 16:24:46 +010053 0, 2, 4, 6, 8, 10, 12, // +2
54 17, 23, 29, 37, 47, 59, 73, // *1.25
55 97, 127, 167, 223, 293, 389, 521, 691, 919, 1223, 1627, 2161, // *1.33
56 3229, 4831, 7243, 10861, 16273, 24407, 36607, 54907, // *1.5
57};
Damien660365e2013-12-17 18:27:24 +000058
Damien George00137b82016-04-15 16:24:46 +010059STATIC mp_uint_t get_hash_alloc_greater_or_equal_to(mp_uint_t x) {
60 for (size_t i = 0; i < MP_ARRAY_SIZE(hash_allocation_sizes); i++) {
61 if (hash_allocation_sizes[i] >= x) {
62 return hash_allocation_sizes[i];
Damien660365e2013-12-17 18:27:24 +000063 }
64 }
65 // ran out of primes in the table!
66 // return something sensible, at least make it odd
Damien George00137b82016-04-15 16:24:46 +010067 return (x + x / 2) | 1;
Damien660365e2013-12-17 18:27:24 +000068}
69
Damiend99b0522013-12-21 18:17:45 +000070/******************************************************************************/
71/* map */
72
Damien George93965e72014-08-30 13:23:35 +010073void mp_map_init(mp_map_t *map, mp_uint_t n) {
Damien George9a58d762014-02-08 18:47:46 +000074 if (n == 0) {
75 map->alloc = 0;
76 map->table = NULL;
77 } else {
Paul Sokolovsky5fedd0c2014-04-06 21:00:58 +030078 map->alloc = n;
Damien George9a58d762014-02-08 18:47:46 +000079 map->table = m_new0(mp_map_elem_t, map->alloc);
80 }
Damien George38a2da62014-01-08 17:33:12 +000081 map->used = 0;
82 map->all_keys_are_qstrs = 1;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020083 map->is_fixed = 0;
84 map->is_ordered = 0;
Damien George9a58d762014-02-08 18:47:46 +000085}
86
Damien George93965e72014-08-30 13:23:35 +010087void mp_map_init_fixed_table(mp_map_t *map, mp_uint_t n, const mp_obj_t *table) {
Damien George9a58d762014-02-08 18:47:46 +000088 map->alloc = n;
89 map->used = n;
90 map->all_keys_are_qstrs = 1;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +020091 map->is_fixed = 1;
92 map->is_ordered = 1;
Damien George9a58d762014-02-08 18:47:46 +000093 map->table = (mp_map_elem_t*)table;
Damien660365e2013-12-17 18:27:24 +000094}
95
Damien George93965e72014-08-30 13:23:35 +010096mp_map_t *mp_map_new(mp_uint_t n) {
Damiend99b0522013-12-21 18:17:45 +000097 mp_map_t *map = m_new(mp_map_t, 1);
Damien George38a2da62014-01-08 17:33:12 +000098 mp_map_init(map, n);
Damien660365e2013-12-17 18:27:24 +000099 return map;
100}
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
110void mp_map_free(mp_map_t *map) {
111 mp_map_deinit(map);
112 m_del_obj(mp_map_t, map);
113}
114
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000115void mp_map_clear(mp_map_t *map) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200116 if (!map->is_fixed) {
Damien George9a58d762014-02-08 18:47:46 +0000117 m_del(mp_map_elem_t, map->table, map->alloc);
118 }
119 map->alloc = 0;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000120 map->used = 0;
Damien George38a2da62014-01-08 17:33:12 +0000121 map->all_keys_are_qstrs = 1;
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200122 map->is_fixed = 0;
Damien George9a58d762014-02-08 18:47:46 +0000123 map->table = NULL;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000124}
125
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200126STATIC void mp_map_rehash(mp_map_t *map) {
Damien George93965e72014-08-30 13:23:35 +0100127 mp_uint_t old_alloc = map->alloc;
Damien George00137b82016-04-15 16:24:46 +0100128 mp_uint_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1);
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000129 mp_map_elem_t *old_table = map->table;
Stephen Kyleb4753272016-04-01 10:51:40 +0100130 mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc);
131 // If we reach this point, table resizing succeeded, now we can edit the old map.
132 map->alloc = new_alloc;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000133 map->used = 0;
Damien George38a2da62014-01-08 17:33:12 +0000134 map->all_keys_are_qstrs = 1;
Stephen Kyleb4753272016-04-01 10:51:40 +0100135 map->table = new_table;
Damien George93965e72014-08-30 13:23:35 +0100136 for (mp_uint_t i = 0; i < old_alloc; i++) {
Damien George95004e52014-04-05 17:17:19 +0100137 if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) {
Damien George38a2da62014-01-08 17:33:12 +0000138 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 +0000139 }
140 }
141 m_del(mp_map_elem_t, old_table, old_alloc);
142}
143
Damien Georged0e82432014-04-05 23:33:12 +0100144// MP_MAP_LOOKUP behaviour:
145// - returns NULL if not found, else the slot it was found in with key,value non-null
146// MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour:
147// - returns slot, with key non-null and value=MP_OBJ_NULL if it was added
148// MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour:
149// - 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 +0100150mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
Damien George186e4632014-04-28 12:11:57 +0100151
Damien George6dde0192015-12-31 00:19:28 +0000152 if (map->is_fixed && lookup_kind != MP_MAP_LOOKUP) {
153 // can't add/remove from a fixed array
154 return NULL;
155 }
156
Damien George186e4632014-04-28 12:11:57 +0100157 // Work out if we can compare just pointers
158 bool compare_only_ptrs = map->all_keys_are_qstrs;
159 if (compare_only_ptrs) {
160 if (MP_OBJ_IS_QSTR(index)) {
161 // Index is a qstr, so can just do ptr comparison.
162 } else if (MP_OBJ_IS_TYPE(index, &mp_type_str)) {
163 // Index is a non-interned string.
164 // We can either intern the string, or force a full equality comparison.
165 // We chose the latter, since interning costs time and potentially RAM,
166 // and it won't necessarily benefit subsequent calls because these calls
167 // most likely won't pass the newly-interned string.
168 compare_only_ptrs = false;
Damien Georged1cee022015-03-20 17:41:37 +0000169 } else if (lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George186e4632014-04-28 12:11:57 +0100170 // If we are not adding, then we can return straight away a failed
171 // lookup because we know that the index will never be found.
172 return NULL;
173 }
174 }
175
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200176 // if the map is an ordered array then we must do a brute force linear search
177 if (map->is_ordered) {
Damien George9a58d762014-02-08 18:47:46 +0000178 for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
Damien George186e4632014-04-28 12:11:57 +0100179 if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) {
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200180 if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) {
181 elem->key = MP_OBJ_SENTINEL;
182 // keep elem->value so that caller can access it if needed
183 }
Damien George9a58d762014-02-08 18:47:46 +0000184 return elem;
185 }
186 }
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200187 if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) {
188 return NULL;
189 }
190 // TODO shrink array down over any previously-freed slots
191 if (map->used == map->alloc) {
192 // TODO: Alloc policy
193 map->alloc += 4;
194 map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc);
195 mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table));
196 }
197 mp_map_elem_t *elem = map->table + map->used++;
198 elem->key = index;
199 if (!MP_OBJ_IS_QSTR(index)) {
200 map->all_keys_are_qstrs = 0;
201 }
202 return elem;
Damien George9a58d762014-02-08 18:47:46 +0000203 }
204
Paul Sokolovsky0ef01d02015-03-18 01:25:04 +0200205 // map is a hash table (not an ordered array), so do a hash lookup
Damien George9a58d762014-02-08 18:47:46 +0000206
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000207 if (map->alloc == 0) {
Damien Georged1cee022015-03-20 17:41:37 +0000208 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000209 mp_map_rehash(map);
210 } else {
211 return NULL;
212 }
213 }
Damien George9a58d762014-02-08 18:47:46 +0000214
Damien Georgebbe8d512015-12-26 21:15:47 +0000215 // get hash of index, with fast path for common case of qstr
216 mp_uint_t hash;
217 if (MP_OBJ_IS_QSTR(index)) {
218 hash = qstr_hash(MP_OBJ_QSTR_VALUE(index));
219 } else {
220 hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
221 }
222
Damien George93965e72014-08-30 13:23:35 +0100223 mp_uint_t pos = hash % map->alloc;
224 mp_uint_t start_pos = pos;
Damien George95004e52014-04-05 17:17:19 +0100225 mp_map_elem_t *avail_slot = NULL;
Damien660365e2013-12-17 18:27:24 +0000226 for (;;) {
Damien George95004e52014-04-05 17:17:19 +0100227 mp_map_elem_t *slot = &map->table[pos];
228 if (slot->key == MP_OBJ_NULL) {
229 // found NULL slot, so index is not in table
Damien Georged1cee022015-03-20 17:41:37 +0000230 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100231 map->used += 1;
232 if (avail_slot == NULL) {
233 avail_slot = slot;
Damien660365e2013-12-17 18:27:24 +0000234 }
Damien George593faf12015-11-19 01:27:28 +0000235 avail_slot->key = index;
236 avail_slot->value = MP_OBJ_NULL;
Damien George95004e52014-04-05 17:17:19 +0100237 if (!MP_OBJ_IS_QSTR(index)) {
238 map->all_keys_are_qstrs = 0;
239 }
Damien George593faf12015-11-19 01:27:28 +0000240 return avail_slot;
Damien George95004e52014-04-05 17:17:19 +0100241 } else {
Damien Georged0e82432014-04-05 23:33:12 +0100242 return NULL;
Damien660365e2013-12-17 18:27:24 +0000243 }
Damien George95004e52014-04-05 17:17:19 +0100244 } else if (slot->key == MP_OBJ_SENTINEL) {
245 // found deleted slot, remember for later
246 if (avail_slot == NULL) {
247 avail_slot = slot;
Damien660365e2013-12-17 18:27:24 +0000248 }
Damien George186e4632014-04-28 12:11:57 +0100249 } else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) {
Damien George95004e52014-04-05 17:17:19 +0100250 // found index
251 // Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x
Damien Georged1cee022015-03-20 17:41:37 +0000252 if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100253 // delete element in this slot
254 map->used--;
255 if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) {
256 // optimisation if next slot is empty
257 slot->key = MP_OBJ_NULL;
258 } else {
259 slot->key = MP_OBJ_SENTINEL;
260 }
Damien Georged0e82432014-04-05 23:33:12 +0100261 // keep slot->value so that caller can access it if needed
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000262 }
Damien George95004e52014-04-05 17:17:19 +0100263 return slot;
Damien660365e2013-12-17 18:27:24 +0000264 }
Paul Sokolovsky4a088f42014-04-05 04:17:17 +0300265
266 // not yet found, keep searching in this table
267 pos = (pos + 1) % map->alloc;
Damien George95004e52014-04-05 17:17:19 +0100268
269 if (pos == start_pos) {
270 // search got back to starting position, so index is not in table
Damien Georged1cee022015-03-20 17:41:37 +0000271 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100272 if (avail_slot != NULL) {
273 // there was an available slot, so use that
274 map->used++;
275 avail_slot->key = index;
276 avail_slot->value = MP_OBJ_NULL;
277 if (!MP_OBJ_IS_QSTR(index)) {
278 map->all_keys_are_qstrs = 0;
279 }
280 return avail_slot;
281 } else {
282 // not enough room in table, rehash it
283 mp_map_rehash(map);
284 // restart the search for the new element
285 start_pos = pos = hash % map->alloc;
286 }
287 } else {
Damien Georged0e82432014-04-05 23:33:12 +0100288 return NULL;
Damien George95004e52014-04-05 17:17:19 +0100289 }
290 }
Damien660365e2013-12-17 18:27:24 +0000291 }
292}
293
Damiend99b0522013-12-21 18:17:45 +0000294/******************************************************************************/
295/* set */
296
Damien Georgee37dcaa2014-12-27 17:07:16 +0000297#if MICROPY_PY_BUILTINS_SET
298
Damien George93965e72014-08-30 13:23:35 +0100299void mp_set_init(mp_set_t *set, mp_uint_t n) {
Damien George2bfd2dc2014-04-07 01:16:17 +0100300 set->alloc = n;
Damiend99b0522013-12-21 18:17:45 +0000301 set->used = 0;
302 set->table = m_new0(mp_obj_t, set->alloc);
Damien660365e2013-12-17 18:27:24 +0000303}
304
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200305STATIC void mp_set_rehash(mp_set_t *set) {
Damien George93965e72014-08-30 13:23:35 +0100306 mp_uint_t old_alloc = set->alloc;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000307 mp_obj_t *old_table = set->table;
Damien George00137b82016-04-15 16:24:46 +0100308 set->alloc = get_hash_alloc_greater_or_equal_to(set->alloc + 1);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000309 set->used = 0;
310 set->table = m_new0(mp_obj_t, set->alloc);
Damien George93965e72014-08-30 13:23:35 +0100311 for (mp_uint_t i = 0; i < old_alloc; i++) {
Damien George95004e52014-04-05 17:17:19 +0100312 if (old_table[i] != MP_OBJ_NULL && old_table[i] != MP_OBJ_SENTINEL) {
313 mp_set_lookup(set, old_table[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000314 }
315 }
316 m_del(mp_obj_t, old_table, old_alloc);
317}
318
John R. Lenton2a241722014-01-12 16:39:39 +0000319mp_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 +0000320 // Note: lookup_kind can be MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND which
321 // is handled by using bitwise operations.
322
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000323 if (set->alloc == 0) {
John R. Lentonae00d332014-01-12 18:23:36 +0000324 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000325 mp_set_rehash(set);
326 } else {
Damien George83229d32015-11-20 14:09:20 +0000327 return MP_OBJ_NULL;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000328 }
329 }
Damien Georgec2a4e4e2015-05-11 12:25:19 +0000330 mp_uint_t hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
Damien George93965e72014-08-30 13:23:35 +0100331 mp_uint_t pos = hash % set->alloc;
332 mp_uint_t start_pos = pos;
Damien George95004e52014-04-05 17:17:19 +0100333 mp_obj_t *avail_slot = NULL;
Damien660365e2013-12-17 18:27:24 +0000334 for (;;) {
Damiend99b0522013-12-21 18:17:45 +0000335 mp_obj_t elem = set->table[pos];
336 if (elem == MP_OBJ_NULL) {
Damien George95004e52014-04-05 17:17:19 +0100337 // found NULL slot, so index is not in table
John R. Lentonae00d332014-01-12 18:23:36 +0000338 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100339 if (avail_slot == NULL) {
340 avail_slot = &set->table[pos];
Damien660365e2013-12-17 18:27:24 +0000341 }
Damien George95004e52014-04-05 17:17:19 +0100342 set->used++;
343 *avail_slot = index;
344 return index;
Damien660365e2013-12-17 18:27:24 +0000345 } else {
Damiend99b0522013-12-21 18:17:45 +0000346 return MP_OBJ_NULL;
Damien660365e2013-12-17 18:27:24 +0000347 }
Damien George95004e52014-04-05 17:17:19 +0100348 } else if (elem == MP_OBJ_SENTINEL) {
349 // found deleted slot, remember for later
350 if (avail_slot == NULL) {
351 avail_slot = &set->table[pos];
352 }
353 } else if (mp_obj_equal(elem, index)) {
354 // found index
John R. Lentonae00d332014-01-12 18:23:36 +0000355 if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100356 // delete element
John R. Lenton2a241722014-01-12 16:39:39 +0000357 set->used--;
Damien George95004e52014-04-05 17:17:19 +0100358 if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
359 // optimisation if next slot is empty
360 set->table[pos] = MP_OBJ_NULL;
361 } else {
362 set->table[pos] = MP_OBJ_SENTINEL;
363 }
John R. Lenton2a241722014-01-12 16:39:39 +0000364 }
Damien660365e2013-12-17 18:27:24 +0000365 return elem;
Damien George95004e52014-04-05 17:17:19 +0100366 }
367
368 // not yet found, keep searching in this table
369 pos = (pos + 1) % set->alloc;
370
371 if (pos == start_pos) {
372 // search got back to starting position, so index is not in table
373 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
374 if (avail_slot != NULL) {
375 // there was an available slot, so use that
376 set->used++;
377 *avail_slot = index;
378 return index;
379 } else {
380 // not enough room in table, rehash it
381 mp_set_rehash(set);
382 // restart the search for the new element
383 start_pos = pos = hash % set->alloc;
384 }
385 } else {
386 return MP_OBJ_NULL;
387 }
Damien660365e2013-12-17 18:27:24 +0000388 }
389 }
390}
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000391
Damien George95004e52014-04-05 17:17:19 +0100392mp_obj_t mp_set_remove_first(mp_set_t *set) {
Damien George93965e72014-08-30 13:23:35 +0100393 for (mp_uint_t pos = 0; pos < set->alloc; pos++) {
Damien George8b0535e2014-04-05 21:53:54 +0100394 if (MP_SET_SLOT_IS_FILLED(set, pos)) {
Damien George95004e52014-04-05 17:17:19 +0100395 mp_obj_t elem = set->table[pos];
396 // delete element
397 set->used--;
398 if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
399 // optimisation if next slot is empty
400 set->table[pos] = MP_OBJ_NULL;
401 } else {
402 set->table[pos] = MP_OBJ_SENTINEL;
403 }
404 return elem;
405 }
406 }
407 return MP_OBJ_NULL;
408}
409
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000410void mp_set_clear(mp_set_t *set) {
Damien George9a58d762014-02-08 18:47:46 +0000411 m_del(mp_obj_t, set->table, set->alloc);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000412 set->alloc = 0;
Damien George9a58d762014-02-08 18:47:46 +0000413 set->used = 0;
414 set->table = NULL;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000415}
Paul Sokolovskye3f58c82014-04-05 04:14:22 +0300416
Damien Georgee37dcaa2014-12-27 17:07:16 +0000417#endif // MICROPY_PY_BUILTINS_SET
418
Damien George7860c2a2014-11-05 21:16:41 +0000419#if defined(DEBUG_PRINT) && DEBUG_PRINT
Paul Sokolovskye3f58c82014-04-05 04:14:22 +0300420void mp_map_dump(mp_map_t *map) {
Damien George93965e72014-08-30 13:23:35 +0100421 for (mp_uint_t i = 0; i < map->alloc; i++) {
Paul Sokolovskye3f58c82014-04-05 04:14:22 +0300422 if (map->table[i].key != NULL) {
423 mp_obj_print(map->table[i].key, PRINT_REPR);
424 } else {
425 printf("(nil)");
426 }
427 printf(": %p\n", map->table[i].value);
428 }
429 printf("---\n");
430}
431#endif