blob: 1ce763ab0e251f0cf02197bcd0df0e076fbf99a2 [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
Damien George38a2da62014-01-08 17:33:12 +000029void mp_map_init(mp_map_t *map, int n) {
Damien660365e2013-12-17 18:27:24 +000030 map->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
Damien George38a2da62014-01-08 17:33:12 +000031 map->used = 0;
32 map->all_keys_are_qstrs = 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
Damien George38a2da62014-01-08 17:33:12 +000036mp_map_t *mp_map_new(int n) {
Damiend99b0522013-12-21 18:17:45 +000037 mp_map_t *map = m_new(mp_map_t, 1);
Damien George38a2da62014-01-08 17:33:12 +000038 mp_map_init(map, 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;
Damien George38a2da62014-01-08 17:33:12 +000044 map->all_keys_are_qstrs = 1;
John R. Lenton4ce6cea2014-01-06 17:38:47 +000045 machine_uint_t a = map->alloc;
46 map->alloc = 0;
47 map->table = m_renew(mp_map_elem_t, map->table, a, map->alloc);
48 mp_map_elem_t nul = {NULL, NULL};
49 for (uint i=0; i<map->alloc; i++) {
50 map->table[i] = nul;
51 }
52}
53
Damien George38a2da62014-01-08 17:33:12 +000054static void mp_map_rehash(mp_map_t *map) {
John R. Lenton4ce6cea2014-01-06 17:38:47 +000055 int old_alloc = map->alloc;
56 mp_map_elem_t *old_table = map->table;
57 map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
58 map->used = 0;
Damien George38a2da62014-01-08 17:33:12 +000059 map->all_keys_are_qstrs = 1;
John R. Lenton4ce6cea2014-01-06 17:38:47 +000060 map->table = m_new0(mp_map_elem_t, map->alloc);
61 for (int i = 0; i < old_alloc; i++) {
62 if (old_table[i].key != NULL) {
Damien George38a2da62014-01-08 17:33:12 +000063 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 +000064 }
65 }
66 m_del(mp_map_elem_t, old_table, old_alloc);
67}
68
Damien George38a2da62014-01-08 17:33:12 +000069mp_map_elem_t* mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
Damien660365e2013-12-17 18:27:24 +000070 machine_uint_t hash;
Damien George38a2da62014-01-08 17:33:12 +000071 hash = mp_obj_hash(index);
John R. Lenton4ce6cea2014-01-06 17:38:47 +000072 if (map->alloc == 0) {
Damien George38a2da62014-01-08 17:33:12 +000073 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton4ce6cea2014-01-06 17:38:47 +000074 mp_map_rehash(map);
75 } else {
76 return NULL;
77 }
78 }
Damien660365e2013-12-17 18:27:24 +000079 uint pos = hash % map->alloc;
80 for (;;) {
Damiend99b0522013-12-21 18:17:45 +000081 mp_map_elem_t *elem = &map->table[pos];
Damien660365e2013-12-17 18:27:24 +000082 if (elem->key == NULL) {
83 // not in table
Damien George38a2da62014-01-08 17:33:12 +000084 if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damien660365e2013-12-17 18:27:24 +000085 if (map->used + 1 >= map->alloc) {
86 // not enough room in table, rehash it
John R. Lenton4ce6cea2014-01-06 17:38:47 +000087 mp_map_rehash(map);
Damien660365e2013-12-17 18:27:24 +000088 // restart the search for the new element
89 pos = hash % map->alloc;
90 } else {
91 map->used += 1;
92 elem->key = index;
Damien George38a2da62014-01-08 17:33:12 +000093 if (!MP_OBJ_IS_QSTR(index)) {
94 map->all_keys_are_qstrs = 0;
95 }
Damien660365e2013-12-17 18:27:24 +000096 return elem;
97 }
98 } else {
99 return NULL;
100 }
Damien George38a2da62014-01-08 17:33:12 +0000101 } else if (elem->key == index || (!map->all_keys_are_qstrs && 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 */
Damien George38a2da62014-01-08 17:33:12 +0000108 if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000109 map->used--;
Damien George38a2da62014-01-08 17:33:12 +0000110 // this leaks this memory (but see dict_get_helper)
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000111 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;
John R. Lentonbe8fe5b2014-01-06 20:25:51 +0000115 elem->value = NULL;
John R. Lenton0fcbaa42014-01-06 19:48:34 +0000116 return retval;
117 }
Damien660365e2013-12-17 18:27:24 +0000118 return elem;
119 } else {
120 // not yet found, keep searching in this table
121 pos = (pos + 1) % map->alloc;
122 }
123 }
124}
125
Damiend99b0522013-12-21 18:17:45 +0000126/******************************************************************************/
127/* set */
128
129void mp_set_init(mp_set_t *set, int n) {
130 set->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
131 set->used = 0;
132 set->table = m_new0(mp_obj_t, set->alloc);
Damien660365e2013-12-17 18:27:24 +0000133}
134
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000135static void mp_set_rehash(mp_set_t *set) {
136 int old_alloc = set->alloc;
137 mp_obj_t *old_table = set->table;
138 set->alloc = get_doubling_prime_greater_or_equal_to(set->alloc + 1);
139 set->used = 0;
140 set->table = m_new0(mp_obj_t, set->alloc);
141 for (int i = 0; i < old_alloc; i++) {
142 if (old_table[i] != NULL) {
143 mp_set_lookup(set, old_table[i], true);
144 }
145 }
146 m_del(mp_obj_t, old_table, old_alloc);
147}
148
John R. Lenton2a241722014-01-12 16:39:39 +0000149mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
John R. Lentonae00d332014-01-12 18:23:36 +0000150 int hash;
151 int pos;
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000152 if (set->alloc == 0) {
John R. Lentonae00d332014-01-12 18:23:36 +0000153 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000154 mp_set_rehash(set);
155 } else {
156 return NULL;
157 }
158 }
John R. Lentonae00d332014-01-12 18:23:36 +0000159 if (lookup_kind & MP_MAP_LOOKUP_FIRST) {
160 hash = 0;
161 pos = 0;
162 } else {
163 hash = mp_obj_hash(index);;
164 pos = hash % set->alloc;
165 }
Damien660365e2013-12-17 18:27:24 +0000166 for (;;) {
Damiend99b0522013-12-21 18:17:45 +0000167 mp_obj_t elem = set->table[pos];
168 if (elem == MP_OBJ_NULL) {
Damien660365e2013-12-17 18:27:24 +0000169 // not in table
John R. Lentonae00d332014-01-12 18:23:36 +0000170 if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
Damiend99b0522013-12-21 18:17:45 +0000171 if (set->used + 1 >= set->alloc) {
Damien660365e2013-12-17 18:27:24 +0000172 // not enough room in table, rehash it
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000173 mp_set_rehash(set);
Damien660365e2013-12-17 18:27:24 +0000174 // restart the search for the new element
Damiend99b0522013-12-21 18:17:45 +0000175 pos = hash % set->alloc;
Damien660365e2013-12-17 18:27:24 +0000176 } else {
Damiend99b0522013-12-21 18:17:45 +0000177 set->used += 1;
178 set->table[pos] = index;
Damien660365e2013-12-17 18:27:24 +0000179 return index;
180 }
John R. Lentonae00d332014-01-12 18:23:36 +0000181 } else if (lookup_kind & MP_MAP_LOOKUP_FIRST) {
182 pos++;
Damien660365e2013-12-17 18:27:24 +0000183 } else {
Damiend99b0522013-12-21 18:17:45 +0000184 return MP_OBJ_NULL;
Damien660365e2013-12-17 18:27:24 +0000185 }
John R. Lentonae00d332014-01-12 18:23:36 +0000186 } else if (lookup_kind & MP_MAP_LOOKUP_FIRST || mp_obj_equal(elem, index)) {
Damien660365e2013-12-17 18:27:24 +0000187 // found it
John R. Lentonae00d332014-01-12 18:23:36 +0000188 if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
John R. Lenton2a241722014-01-12 16:39:39 +0000189 set->used--;
190 set->table[pos] = NULL;
John R. Lenton2a241722014-01-12 16:39:39 +0000191 }
Damien660365e2013-12-17 18:27:24 +0000192 return elem;
193 } else {
194 // not yet found, keep searching in this table
Damiend99b0522013-12-21 18:17:45 +0000195 pos = (pos + 1) % set->alloc;
Damien660365e2013-12-17 18:27:24 +0000196 }
197 }
198}
John R. Lenton1d7fb2f2014-01-12 15:44:26 +0000199
200void mp_set_clear(mp_set_t *set) {
201 set->used = 0;
202 machine_uint_t a = set->alloc;
203 set->alloc = 0;
204 set->table = m_renew(mp_obj_t, set->table, a, set->alloc);
205 for (uint i=0; i<set->alloc; i++) {
206 set->table[i] = NULL;
207 }
208}