blob: 82237381ccbae39e7d09b596b3715aa5c27aa71f [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
Damien660365e2013-12-17 18:27:24 +000027#include <stdlib.h>
Damien George8b0535e2014-04-05 21:53:54 +010028#include <assert.h>
Damien660365e2013-12-17 18:27:24 +000029
Damiend99b0522013-12-21 18:17:45 +000030#include "mpconfig.h"
Paul Sokolovsky59c675a2014-06-21 22:43:22 +030031#include "misc.h"
Damien George55baff42014-01-21 21:40:13 +000032#include "qstr.h"
Damien660365e2013-12-17 18:27:24 +000033#include "obj.h"
Damiend99b0522013-12-21 18:17:45 +000034#include "runtime0.h"
Damien660365e2013-12-17 18:27:24 +000035
36// approximatelly doubling primes; made with Mathematica command: Table[Prime[Floor[(1.7)^n]], {n, 3, 24}]
John R. Lenton4ce6cea2014-01-06 17:38:47 +000037// prefixed with zero for the empty case.
Paul Sokolovsky520e2f52014-02-12 18:31:30 +020038STATIC int doubling_primes[] = {0, 7, 19, 43, 89, 179, 347, 647, 1229, 2297, 4243, 7829, 14347, 26017, 47149, 84947, 152443, 273253, 488399, 869927, 1547173, 2745121, 4861607};
Damien660365e2013-12-17 18:27:24 +000039
Damien Georgedf6567e2014-03-30 13:54:02 +010040STATIC int get_doubling_prime_greater_or_equal_to(int x) {
Damien660365e2013-12-17 18:27:24 +000041 for (int i = 0; i < sizeof(doubling_primes) / sizeof(int); i++) {
42 if (doubling_primes[i] >= x) {
43 return doubling_primes[i];
44 }
45 }
46 // ran out of primes in the table!
47 // return something sensible, at least make it odd
48 return x | 1;
49}
50
Damiend99b0522013-12-21 18:17:45 +000051/******************************************************************************/
52/* map */
53
Damien George38a2da62014-01-08 17:33:12 +000054void mp_map_init(mp_map_t *map, int n) {
Damien George9a58d762014-02-08 18:47:46 +000055 if (n == 0) {
56 map->alloc = 0;
57 map->table = NULL;
58 } else {
Paul Sokolovsky5fedd0c2014-04-06 21:00:58 +030059 map->alloc = n;
Damien George9a58d762014-02-08 18:47:46 +000060 map->table = m_new0(mp_map_elem_t, map->alloc);
61 }
Damien George38a2da62014-01-08 17:33:12 +000062 map->used = 0;
63 map->all_keys_are_qstrs = 1;
Damien George9a58d762014-02-08 18:47:46 +000064 map->table_is_fixed_array = 0;
65}
66
67void mp_map_init_fixed_table(mp_map_t *map, int n, const mp_obj_t *table) {
68 map->alloc = n;
69 map->used = n;
70 map->all_keys_are_qstrs = 1;
71 map->table_is_fixed_array = 1;
72 map->table = (mp_map_elem_t*)table;
Damien660365e2013-12-17 18:27:24 +000073}
74
Damien George38a2da62014-01-08 17:33:12 +000075mp_map_t *mp_map_new(int n) {
Damiend99b0522013-12-21 18:17:45 +000076 mp_map_t *map = m_new(mp_map_t, 1);
Damien George38a2da62014-01-08 17:33:12 +000077 mp_map_init(map, n);
Damien660365e2013-12-17 18:27:24 +000078 return map;
79}
80
Paul Sokolovsky9a24a042014-01-25 00:02:20 +020081// Differentiate from mp_map_clear() - semantics is different
82void mp_map_deinit(mp_map_t *map) {
Damien George9a58d762014-02-08 18:47:46 +000083 if (!map->table_is_fixed_array) {
84 m_del(mp_map_elem_t, map->table, map->alloc);
85 }
Paul Sokolovsky9a24a042014-01-25 00:02:20 +020086 map->used = map->alloc = 0;
87}
88
89void mp_map_free(mp_map_t *map) {
90 mp_map_deinit(map);
91 m_del_obj(mp_map_t, map);
92}
93
John R. Lenton4ce6cea2014-01-06 17:38:47 +000094void mp_map_clear(mp_map_t *map) {
Damien George9a58d762014-02-08 18:47:46 +000095 if (!map->table_is_fixed_array) {
96 m_del(mp_map_elem_t, map->table, map->alloc);
97 }
98 map->alloc = 0;
John R. Lenton4ce6cea2014-01-06 17:38:47 +000099 map->used = 0;
Damien George38a2da62014-01-08 17:33:12 +0000100 map->all_keys_are_qstrs = 1;
Damien George9a58d762014-02-08 18:47:46 +0000101 map->table_is_fixed_array = 0;
102 map->table = NULL;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000103}
104
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200105STATIC void mp_map_rehash(mp_map_t *map) {
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000106 int old_alloc = map->alloc;
107 mp_map_elem_t *old_table = map->table;
108 map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
109 map->used = 0;
Damien George38a2da62014-01-08 17:33:12 +0000110 map->all_keys_are_qstrs = 1;
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000111 map->table = m_new0(mp_map_elem_t, map->alloc);
112 for (int i = 0; i < old_alloc; i++) {
Damien George95004e52014-04-05 17:17:19 +0100113 if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) {
Damien George38a2da62014-01-08 17:33:12 +0000114 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 +0000115 }
116 }
117 m_del(mp_map_elem_t, old_table, old_alloc);
118}
119
Damien Georged0e82432014-04-05 23:33:12 +0100120// MP_MAP_LOOKUP behaviour:
121// - returns NULL if not found, else the slot it was found in with key,value non-null
122// MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour:
123// - returns slot, with key non-null and value=MP_OBJ_NULL if it was added
124// MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour:
125// - returns NULL if not found, else the slot if was found in with key null and value non-null
Damien George38a2da62014-01-08 17:33:12 +0000126mp_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 +0100127
128 // Work out if we can compare just pointers
129 bool compare_only_ptrs = map->all_keys_are_qstrs;
130 if (compare_only_ptrs) {
131 if (MP_OBJ_IS_QSTR(index)) {
132 // Index is a qstr, so can just do ptr comparison.
133 } else if (MP_OBJ_IS_TYPE(index, &mp_type_str)) {
134 // Index is a non-interned string.
135 // We can either intern the string, or force a full equality comparison.
136 // We chose the latter, since interning costs time and potentially RAM,
137 // and it won't necessarily benefit subsequent calls because these calls
138 // most likely won't pass the newly-interned string.
139 compare_only_ptrs = false;
140 } else if (!(lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) {
141 // If we are not adding, then we can return straight away a failed
142 // lookup because we know that the index will never be found.
143 return NULL;
144 }
145 }
146
Damien George9a58d762014-02-08 18:47:46 +0000147 // if the map is a fixed array then we must do a brute force linear search
148 if (map->table_is_fixed_array) {
149 if (lookup_kind != MP_MAP_LOOKUP) {
150 return NULL;
151 }
152 for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
Damien George186e4632014-04-28 12:11:57 +0100153 if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) {
Damien George9a58d762014-02-08 18:47:46 +0000154 return elem;
155 }
156 }
157 return NULL;
158 }
159
160 // map is a hash table (not a fixed array), so do a hash lookup
161
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000162 if (map->alloc == 0) {
Damien George9a58d762014-02-08 18:47:46 +0000163 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000164 mp_map_rehash(map);
165 } else {
166 return NULL;
167 }
168 }
Damien George9a58d762014-02-08 18:47:46 +0000169
Damien George40f3c022014-07-03 13:25:24 +0100170 mp_uint_t hash = mp_obj_hash(index);
Damien660365e2013-12-17 18:27:24 +0000171 uint pos = hash % map->alloc;
Damien George95004e52014-04-05 17:17:19 +0100172 uint start_pos = pos;
173 mp_map_elem_t *avail_slot = NULL;
Damien660365e2013-12-17 18:27:24 +0000174 for (;;) {
Damien George95004e52014-04-05 17:17:19 +0100175 mp_map_elem_t *slot = &map->table[pos];
176 if (slot->key == MP_OBJ_NULL) {
177 // found NULL slot, so index is not in table
Damien George9a58d762014-02-08 18:47:46 +0000178 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100179 map->used += 1;
180 if (avail_slot == NULL) {
181 avail_slot = slot;
Damien660365e2013-12-17 18:27:24 +0000182 }
Damien George95004e52014-04-05 17:17:19 +0100183 slot->key = index;
184 slot->value = MP_OBJ_NULL;
185 if (!MP_OBJ_IS_QSTR(index)) {
186 map->all_keys_are_qstrs = 0;
187 }
188 return slot;
189 } else {
Damien Georged0e82432014-04-05 23:33:12 +0100190 return NULL;
Damien660365e2013-12-17 18:27:24 +0000191 }
Damien George95004e52014-04-05 17:17:19 +0100192 } else if (slot->key == MP_OBJ_SENTINEL) {
193 // found deleted slot, remember for later
194 if (avail_slot == NULL) {
195 avail_slot = slot;
Damien660365e2013-12-17 18:27:24 +0000196 }
Damien George186e4632014-04-28 12:11:57 +0100197 } else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) {
Damien George95004e52014-04-05 17:17:19 +0100198 // found index
199 // Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x
Damien George9a58d762014-02-08 18:47:46 +0000200 if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100201 // delete element in this slot
202 map->used--;
203 if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) {
204 // optimisation if next slot is empty
205 slot->key = MP_OBJ_NULL;
206 } else {
207 slot->key = MP_OBJ_SENTINEL;
208 }
Damien Georged0e82432014-04-05 23:33:12 +0100209 // keep slot->value so that caller can access it if needed
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000210 }
Damien George95004e52014-04-05 17:17:19 +0100211 return slot;
Damien660365e2013-12-17 18:27:24 +0000212 }
Paul Sokolovsky4a088f42014-04-05 04:17:17 +0300213
214 // not yet found, keep searching in this table
215 pos = (pos + 1) % map->alloc;
Damien George95004e52014-04-05 17:17:19 +0100216
217 if (pos == start_pos) {
218 // search got back to starting position, so index is not in table
219 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
220 if (avail_slot != NULL) {
221 // there was an available slot, so use that
222 map->used++;
223 avail_slot->key = index;
224 avail_slot->value = MP_OBJ_NULL;
225 if (!MP_OBJ_IS_QSTR(index)) {
226 map->all_keys_are_qstrs = 0;
227 }
228 return avail_slot;
229 } else {
230 // not enough room in table, rehash it
231 mp_map_rehash(map);
232 // restart the search for the new element
233 start_pos = pos = hash % map->alloc;
234 }
235 } else {
Damien Georged0e82432014-04-05 23:33:12 +0100236 return NULL;
Damien George95004e52014-04-05 17:17:19 +0100237 }
238 }
Damien660365e2013-12-17 18:27:24 +0000239 }
240}
241
Damiend99b0522013-12-21 18:17:45 +0000242/******************************************************************************/
243/* set */
244
245void mp_set_init(mp_set_t *set, int n) {
Damien George2bfd2dc2014-04-07 01:16:17 +0100246 set->alloc = n;
Damiend99b0522013-12-21 18:17:45 +0000247 set->used = 0;
248 set->table = m_new0(mp_obj_t, set->alloc);
Damien660365e2013-12-17 18:27:24 +0000249}
250
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200251STATIC void mp_set_rehash(mp_set_t *set) {
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000252 int old_alloc = set->alloc;
253 mp_obj_t *old_table = set->table;
254 set->alloc = get_doubling_prime_greater_or_equal_to(set->alloc + 1);
255 set->used = 0;
256 set->table = m_new0(mp_obj_t, set->alloc);
257 for (int i = 0; i < old_alloc; i++) {
Damien George95004e52014-04-05 17:17:19 +0100258 if (old_table[i] != MP_OBJ_NULL && old_table[i] != MP_OBJ_SENTINEL) {
259 mp_set_lookup(set, old_table[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000260 }
261 }
262 m_del(mp_obj_t, old_table, old_alloc);
263}
264
John R. Lenton2a241722014-01-12 16:39:39 +0000265mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000266 if (set->alloc == 0) {
John R. Lentonae00d332014-01-12 18:23:36 +0000267 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000268 mp_set_rehash(set);
269 } else {
270 return NULL;
271 }
272 }
Damien George40f3c022014-07-03 13:25:24 +0100273 mp_uint_t hash = mp_obj_hash(index);
Damien George95004e52014-04-05 17:17:19 +0100274 uint pos = hash % set->alloc;
275 uint start_pos = pos;
276 mp_obj_t *avail_slot = NULL;
Damien660365e2013-12-17 18:27:24 +0000277 for (;;) {
Damiend99b0522013-12-21 18:17:45 +0000278 mp_obj_t elem = set->table[pos];
279 if (elem == MP_OBJ_NULL) {
Damien George95004e52014-04-05 17:17:19 +0100280 // found NULL slot, so index is not in table
John R. Lentonae00d332014-01-12 18:23:36 +0000281 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100282 if (avail_slot == NULL) {
283 avail_slot = &set->table[pos];
Damien660365e2013-12-17 18:27:24 +0000284 }
Damien George95004e52014-04-05 17:17:19 +0100285 set->used++;
286 *avail_slot = index;
287 return index;
Damien660365e2013-12-17 18:27:24 +0000288 } else {
Damiend99b0522013-12-21 18:17:45 +0000289 return MP_OBJ_NULL;
Damien660365e2013-12-17 18:27:24 +0000290 }
Damien George95004e52014-04-05 17:17:19 +0100291 } else if (elem == MP_OBJ_SENTINEL) {
292 // found deleted slot, remember for later
293 if (avail_slot == NULL) {
294 avail_slot = &set->table[pos];
295 }
296 } else if (mp_obj_equal(elem, index)) {
297 // found index
John R. Lentonae00d332014-01-12 18:23:36 +0000298 if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
Damien George95004e52014-04-05 17:17:19 +0100299 // delete element
John R. Lenton2a241722014-01-12 16:39:39 +0000300 set->used--;
Damien George95004e52014-04-05 17:17:19 +0100301 if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
302 // optimisation if next slot is empty
303 set->table[pos] = MP_OBJ_NULL;
304 } else {
305 set->table[pos] = MP_OBJ_SENTINEL;
306 }
John R. Lenton2a241722014-01-12 16:39:39 +0000307 }
Damien660365e2013-12-17 18:27:24 +0000308 return elem;
Damien George95004e52014-04-05 17:17:19 +0100309 }
310
311 // not yet found, keep searching in this table
312 pos = (pos + 1) % set->alloc;
313
314 if (pos == start_pos) {
315 // search got back to starting position, so index is not in table
316 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
317 if (avail_slot != NULL) {
318 // there was an available slot, so use that
319 set->used++;
320 *avail_slot = index;
321 return index;
322 } else {
323 // not enough room in table, rehash it
324 mp_set_rehash(set);
325 // restart the search for the new element
326 start_pos = pos = hash % set->alloc;
327 }
328 } else {
329 return MP_OBJ_NULL;
330 }
Damien660365e2013-12-17 18:27:24 +0000331 }
332 }
333}
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000334
Damien George95004e52014-04-05 17:17:19 +0100335mp_obj_t mp_set_remove_first(mp_set_t *set) {
336 for (uint pos = 0; pos < set->alloc; pos++) {
Damien George8b0535e2014-04-05 21:53:54 +0100337 if (MP_SET_SLOT_IS_FILLED(set, pos)) {
Damien George95004e52014-04-05 17:17:19 +0100338 mp_obj_t elem = set->table[pos];
339 // delete element
340 set->used--;
341 if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
342 // optimisation if next slot is empty
343 set->table[pos] = MP_OBJ_NULL;
344 } else {
345 set->table[pos] = MP_OBJ_SENTINEL;
346 }
347 return elem;
348 }
349 }
350 return MP_OBJ_NULL;
351}
352
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000353void mp_set_clear(mp_set_t *set) {
Damien George9a58d762014-02-08 18:47:46 +0000354 m_del(mp_obj_t, set->table, set->alloc);
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000355 set->alloc = 0;
Damien George9a58d762014-02-08 18:47:46 +0000356 set->used = 0;
357 set->table = NULL;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000358}
Paul Sokolovskye3f58c82014-04-05 04:14:22 +0300359
360#if DEBUG_PRINT
361void mp_map_dump(mp_map_t *map) {
362 for (int i = 0; i < map->alloc; i++) {
363 if (map->table[i].key != NULL) {
364 mp_obj_print(map->table[i].key, PRINT_REPR);
365 } else {
366 printf("(nil)");
367 }
368 printf(": %p\n", map->table[i].value);
369 }
370 printf("---\n");
371}
372#endif