blob: 8554150adf6a1c3bea295a0f148c206a25cc3dbc [file] [log] [blame]
Damien660365e2013-12-17 18:27:24 +00001#include <stdint.h>
2#include <stdlib.h>
3#include <assert.h>
4
5#include "misc.h"
Damiend99b0522013-12-21 18:17:45 +00006#include "mpconfig.h"
Damien660365e2013-12-17 18:27:24 +00007#include "obj.h"
Damiend99b0522013-12-21 18:17:45 +00008#include "runtime0.h"
9#include "map.h"
Damien660365e2013-12-17 18:27:24 +000010
11// approximatelly doubling primes; made with Mathematica command: Table[Prime[Floor[(1.7)^n]], {n, 3, 24}]
John R. Lenton4ce6cea2014-01-06 17:38:47 +000012// prefixed with zero for the empty case.
13static 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 +000014
15int get_doubling_prime_greater_or_equal_to(int x) {
16 for (int i = 0; i < sizeof(doubling_primes) / sizeof(int); i++) {
17 if (doubling_primes[i] >= x) {
18 return doubling_primes[i];
19 }
20 }
21 // ran out of primes in the table!
22 // return something sensible, at least make it odd
23 return x | 1;
24}
25
Damiend99b0522013-12-21 18:17:45 +000026/******************************************************************************/
27/* map */
28
29void mp_map_init(mp_map_t *map, mp_map_kind_t kind, int n) {
Damien660365e2013-12-17 18:27:24 +000030 map->kind = kind;
31 map->used = 0;
32 map->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
Damiend99b0522013-12-21 18:17:45 +000033 map->table = m_new0(mp_map_elem_t, map->alloc);
Damien660365e2013-12-17 18:27:24 +000034}
35
Damiend99b0522013-12-21 18:17:45 +000036mp_map_t *mp_map_new(mp_map_kind_t kind, int n) {
37 mp_map_t *map = m_new(mp_map_t, 1);
38 mp_map_init(map, kind, n);
Damien660365e2013-12-17 18:27:24 +000039 return map;
40}
41
John R. Lenton4ce6cea2014-01-06 17:38:47 +000042void mp_map_clear(mp_map_t *map) {
43 map->used = 0;
44 machine_uint_t a = map->alloc;
45 map->alloc = 0;
46 map->table = m_renew(mp_map_elem_t, map->table, a, map->alloc);
47 mp_map_elem_t nul = {NULL, NULL};
48 for (uint i=0; i<map->alloc; i++) {
49 map->table[i] = nul;
50 }
51}
52
53static void mp_map_rehash (mp_map_t *map) {
54 int old_alloc = map->alloc;
55 mp_map_elem_t *old_table = map->table;
56 map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
57 map->used = 0;
58 map->table = m_new0(mp_map_elem_t, map->alloc);
59 for (int i = 0; i < old_alloc; i++) {
60 if (old_table[i].key != NULL) {
John R. Lenton0fcbaa42014-01-06 19:48:34 +000061 mp_map_lookup_helper(map, old_table[i].key, true, false)->value = old_table[i].value;
John R. Lenton4ce6cea2014-01-06 17:38:47 +000062 }
63 }
64 m_del(mp_map_elem_t, old_table, old_alloc);
65}
66
John R. Lenton0fcbaa42014-01-06 19:48:34 +000067mp_map_elem_t* mp_map_lookup_helper(mp_map_t *map, mp_obj_t index, bool add_if_not_found, bool remove_if_found) {
Damiend99b0522013-12-21 18:17:45 +000068 bool is_map_mp_obj = (map->kind == MP_MAP_OBJ);
Damien660365e2013-12-17 18:27:24 +000069 machine_uint_t hash;
Damiend99b0522013-12-21 18:17:45 +000070 if (is_map_mp_obj) {
71 hash = mp_obj_hash(index);
Damien660365e2013-12-17 18:27:24 +000072 } else {
73 hash = (machine_uint_t)index;
74 }
John R. Lenton4ce6cea2014-01-06 17:38:47 +000075 if (map->alloc == 0) {
76 if (add_if_not_found) {
77 mp_map_rehash(map);
78 } else {
79 return NULL;
80 }
81 }
Damien660365e2013-12-17 18:27:24 +000082 uint pos = hash % map->alloc;
83 for (;;) {
Damiend99b0522013-12-21 18:17:45 +000084 mp_map_elem_t *elem = &map->table[pos];
Damien660365e2013-12-17 18:27:24 +000085 if (elem->key == NULL) {
86 // not in table
87 if (add_if_not_found) {
88 if (map->used + 1 >= map->alloc) {
89 // not enough room in table, rehash it
John R. Lenton4ce6cea2014-01-06 17:38:47 +000090 mp_map_rehash(map);
Damien660365e2013-12-17 18:27:24 +000091 // restart the search for the new element
92 pos = hash % map->alloc;
93 } else {
94 map->used += 1;
95 elem->key = index;
96 return elem;
97 }
98 } else {
99 return NULL;
100 }
Damiend99b0522013-12-21 18:17:45 +0000101 } else if (elem->key == index || (is_map_mp_obj && mp_obj_equal(elem->key, index))) {
Damien660365e2013-12-17 18:27:24 +0000102 // found it
103 /* it seems CPython does not replace the index; try x={True:'true'};x[1]='one';x
104 if (add_if_not_found) {
105 elem->key = index;
106 }
107 */
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000108 if (remove_if_found) {
109 map->used--;
110 /* this leaks this memory (but see dict_get_helper) */
111 mp_map_elem_t *retval = m_new(mp_map_elem_t, 1);
112 retval->key = elem->key;
113 retval->value = elem->value;
114 elem->key = NULL;
115 return retval;
116 }
Damien660365e2013-12-17 18:27:24 +0000117 return elem;
118 } else {
119 // not yet found, keep searching in this table
120 pos = (pos + 1) % map->alloc;
121 }
122 }
123}
124
Damiend99b0522013-12-21 18:17:45 +0000125mp_map_elem_t* mp_qstr_map_lookup(mp_map_t *map, qstr index, bool add_if_not_found) {
126 mp_obj_t o = (mp_obj_t)(machine_uint_t)index;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000127 return mp_map_lookup_helper(map, o, add_if_not_found, false);
Damien660365e2013-12-17 18:27:24 +0000128}
129
Damiend99b0522013-12-21 18:17:45 +0000130/******************************************************************************/
131/* set */
132
133void mp_set_init(mp_set_t *set, int n) {
134 set->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
135 set->used = 0;
136 set->table = m_new0(mp_obj_t, set->alloc);
Damien660365e2013-12-17 18:27:24 +0000137}
138
Damiend99b0522013-12-21 18:17:45 +0000139mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, bool add_if_not_found) {
140 int hash = mp_obj_hash(index);
John R. Lenton4ce6cea2014-01-06 17:38:47 +0000141 assert(set->alloc); /* FIXME: if alloc is ever 0 when doing a lookup, this'll fail: */
Damiend99b0522013-12-21 18:17:45 +0000142 int pos = hash % set->alloc;
Damien660365e2013-12-17 18:27:24 +0000143 for (;;) {
Damiend99b0522013-12-21 18:17:45 +0000144 mp_obj_t elem = set->table[pos];
145 if (elem == MP_OBJ_NULL) {
Damien660365e2013-12-17 18:27:24 +0000146 // not in table
147 if (add_if_not_found) {
Damiend99b0522013-12-21 18:17:45 +0000148 if (set->used + 1 >= set->alloc) {
Damien660365e2013-12-17 18:27:24 +0000149 // not enough room in table, rehash it
Damiend99b0522013-12-21 18:17:45 +0000150 int old_alloc = set->alloc;
151 mp_obj_t *old_table = set->table;
152 set->alloc = get_doubling_prime_greater_or_equal_to(set->alloc + 1);
153 set->used = 0;
154 set->table = m_new(mp_obj_t, set->alloc);
Damien660365e2013-12-17 18:27:24 +0000155 for (int i = 0; i < old_alloc; i++) {
156 if (old_table[i] != NULL) {
Damiend99b0522013-12-21 18:17:45 +0000157 mp_set_lookup(set, old_table[i], true);
Damien660365e2013-12-17 18:27:24 +0000158 }
159 }
Damien732407f2013-12-29 19:33:23 +0000160 m_del(mp_obj_t, old_table, old_alloc);
Damien660365e2013-12-17 18:27:24 +0000161 // restart the search for the new element
Damiend99b0522013-12-21 18:17:45 +0000162 pos = hash % set->alloc;
Damien660365e2013-12-17 18:27:24 +0000163 } else {
Damiend99b0522013-12-21 18:17:45 +0000164 set->used += 1;
165 set->table[pos] = index;
Damien660365e2013-12-17 18:27:24 +0000166 return index;
167 }
168 } else {
Damiend99b0522013-12-21 18:17:45 +0000169 return MP_OBJ_NULL;
Damien660365e2013-12-17 18:27:24 +0000170 }
Damiend99b0522013-12-21 18:17:45 +0000171 } else if (mp_obj_equal(elem, index)) {
Damien660365e2013-12-17 18:27:24 +0000172 // found it
173 return elem;
174 } else {
175 // not yet found, keep searching in this table
Damiend99b0522013-12-21 18:17:45 +0000176 pos = (pos + 1) % set->alloc;
Damien660365e2013-12-17 18:27:24 +0000177 }
178 }
179}