8000 py: split runtime into map, obj, builtin. · comfuture/micropython@660365e · GitHub
[go: up one dir, main page]

Skip to content

Commit 660365e

Browse files
committed
py: split runtime into map, obj, builtin.
1 parent a1b2693 commit 660365e

File tree

10 files changed

+1057
-943
lines changed

10 files changed

+1057
-943
lines changed

py/builtin.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#include <stdint.h>
2+
#include <stdlib.h>
3+
#include <stdio.h>
4+
#include <stdarg.h>
5+
#include <string.h>
6+
#include <assert.h>
7+
8+
#include "nlr.h"
9+
#include "misc.h"
10+
#include "mpyconfig.h"
11+
#include "runtime.h"
12+
#include "bc.h"
13+
14+
#include "map.h"
15+
#include "obj.h"
16+
#include "objprivate.h"
17+
#include "builtin.h"
18+
19+
py_obj_t py_builtin___repl_print__(py_obj_t o) {
20+
if (o != py_const_none) {
21+
py_obj_print(o);
22+
printf("\n");
23+
}
24+
return py_const_none;
25+
}
26+
27+
py_obj_t py_builtin_print(int n_args, const py_obj_t* args) {
28+
for (int i = 0; i < n_args; i++) {
29+
if (i > 0) {
30+
printf(" ");
31+
}
32+
if (IS_O(args[i], O_STR)) {
33+
// special case, print string raw
34+
printf("%s", qstr_str(((py_obj_base_t*)args[i])->u_str));
35+
} else {
36+
// print the object Python style
37+
py_obj_print(args[i]);
38+
}
39+
}
40+
printf("\n");
41+
return py_const_none;
42+
}
43+
44+
py_obj_t py_builtin_len(py_obj_t o_in) {
45+
py_small_int_t len = 0;
46+
if (IS_O(o_in, O_STR)) {
47+
py_obj_base_t *o = o_in;
48+
len = strlen(qstr_str(o->u_str));
49+
} else if (IS_O(o_in, O_TUPLE) || IS_O(o_in, O_LIST)) {
50+
py_obj_base_t *o = o_in;
51+
len = o->u_tuple_list.len;
52+
} else if (IS_O(o_in, O_MAP)) {
53+
py_obj_base_t *o = o_in;
54+
len = o->u_map.used;
55+
} else {
56+
assert(0);
57+
}
58+
return TO_SMALL_INT(len);
59+
}
60+
61+
py_obj_t py_builtin_abs(py_obj_t o_in) {
62+
if (IS_SMALL_INT(o_in)) {
63+
py_small_int_t val = FROM_SMALL_INT(o_in);
64+
if (val < 0) {
65+
val = -val;
66+
}
67+
return TO_SMALL_INT(val);
68+
#if MICROPY_ENABLE_FLOAT
69+
} else if (IS_O(o_in, O_FLOAT)) {
70+
py_obj_base_t *o = o_in;
71+
// TODO check for NaN etc
72+
if (o->u_float < 0) {
73+
return py_obj_new_float(-o->u_float);
74+
} else {
75+
return o_in;
76+
}
77+
} else if (IS_O(o_in, O_COMPLEX)) {
78+
py_obj_base_t *o = o_in;
79+
return py_obj_new_float(machine_sqrt(o->u_complex.real*o->u_complex.real + o->u_complex.imag*o->u_complex.imag));
80+
#endif
81+
} else {
82+
assert(0);
83+
return py_const_none;
84+
}
85+
}
86+
87+
py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name) {
88+
// we differ from CPython: we set the new __locals__ object here
89+
py_map_t *old_locals = rt_get_map_locals();
90+
py_map_t *class_locals = py_map_new(MAP_QSTR, 0);
91+
rt_set_map_locals(class_locals);
92+
93+
// call the class code
94+
rt_call_function_1(o_class_fun, (py_obj_t)0xdeadbeef);
95+
96+
// restore old __locals__ object
97+
rt_set_map_locals(old_locals);
98+
99+
// create and return the new class
100+
py_obj_base_t *o = m_new(py_obj_base_t, 1);
101+
o->kind = O_CLASS;
102+
o->u_class.locals = class_locals;
103+
return o;
104+
}
105+
106+
py_obj_t py_builtin_range(int n_args, const py_obj_t* args) {
107+
switch (n_args) {
108+
case 1: return py_obj_new_range(0, py_obj_get_int(args[0]), 1);
109+
case 2: return py_obj_new_range(py_obj_get_int(args[0]), py_obj_get_int(args[1]), 1);
110+
case 3: return py_obj_new_range(py_obj_get_int(args[0]), py_obj_get_int(args[1]), py_obj_get_int(args[2]));
111+
default: nlr_jump(py_obj_new_exception_2(rt_q_TypeError, "range expected at most 3 arguments, got %d", (void*)(machine_int_t)n_args, NULL));
112+
}
113+
}

py/builtin.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
py_obj_t py_builtin___repl_print__(py_obj_t o);
2+
py_obj_t py_builtin_print(int n_args, const py_obj_t* args);
3+
py_obj_t py_builtin_len(py_obj_t o_in);
4+
py_obj_t py_builtin_abs(py_obj_t o_in);
5+
py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name);
6+
py_obj_t py_builtin_range(int n_args, const py_obj_t* args);

py/map.c

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#include <stdint.h>
2+
#include <stdlib.h>
3+
#include <assert.h>
4+
5+
#include "misc.h"
6+
#include "mpyconfig.h"
7+
#include "runtime.h"
8+
9+
#include "map.h"
10+
#include "obj.h"
11+
#include "objprivate.h"
12+
13+
// approximatelly doubling primes; made with Mathematica command: Table[Prime[Floor[(1.7)^n]], {n, 3, 24}]
14+
static int doubling_primes[] = {7, 19, 43, 89, 179, 347, 647, 1229, 2297, 4243, 7829, 14347, 26017, 47149, 84947, 152443, 273253, 488399, 869927, 1547173, 2745121, 4861607};
15+
16+
int get_doubling_prime_greater_or_equal_to(int x) {
17+
for (int i = 0; i < sizeof(doubling_primes) / sizeof(int); i++) {
18+
if (doubling_primes[i] >= x) {
19+
return doubling_primes[i];
20+
}
21+
}
22+
// ran out of primes in the table!
23+
// return something sensible, at least make it odd
24+
return x | 1;
25+
}
26+
27+
void py_map_init(py_map_t *map, py_map_kind_t kind, int n) {
28+
map->kind = kind;
29+
map->used = 0;
30+
map->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
31+
map->table = m_new0(py_map_elem_t, map->alloc);
32+
}
33+
34+
py_map_t *py_map_new(py_map_kind_t kind, int n) {
35+
py_map_t *map = m_new(py_map_t, 1);
36+
py_map_init(map, kind, n);
37+
return map;
38+
}
39+
40+
py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_not_found) {
41+
bool is_map_py_obj = (map->kind == MAP_PY_OBJ);
42+
machine_uint_t hash;
43+
if (is_map_py_obj) {
44+
hash = py_obj_hash(index);
45+
} else {
46+
hash = (machine_uint_t)index;
47+
}
48+
uint pos = hash % map->alloc;
49+
for (;;) {
50+
py_map_elem_t *elem = &map->table[pos];
51+
if (elem->key == NULL) {
52+
// not in table
53+
if (add_if_not_found) {
54+
if (map->used + 1 >= map->alloc) {
55+
// not enough room in table, rehash it
56+
int old_alloc = map->alloc;
57+
py_map_elem_t *old_table = map->table;
58+
map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
59+
map->used = 0;
60+
map->table = m_new0(py_map_elem_t, map->alloc);
61+
for (int i = 0; i < old_alloc; i++) {
62+
if (old_table[i].key != NULL) {
63+
py_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value;
64+
}
65+
}
66+
m_free(old_table);
67+
// restart the search for the new element
68+
pos = hash % map->alloc;
69+
} else {
70+
map->used += 1;
71+
elem->key = index;
72+
return elem;
73+
}
74+
} else {
75+
return NULL;
76+
}
77+
} else if (elem->key == index || (is_map_py_obj && py_obj_equal(elem->key, index))) {
78+
// found it
79+
/* it seems CPython does not replace the index; try x={True:'true'};x[1]='one';x
80+
if (add_if_not_found) {
81+
elem->key = index;
82+
}
83+
*/
84+
return elem;
85+
} else {
86+
// not yet found, keep searching in this table
87+
pos = (pos + 1) % map->alloc;
88+
}
89+
}
90+
}
91+
92+
py_map_elem_t* py_qstr_map_lookup(py_map_t *map, qstr index, bool add_if_not_found) {
93+
py_obj_t o = (py_obj_t)(machine_uint_t)index;
94+
return py_map_lookup_helper(map, o, add_if_not_found);
95+
}
96+
97+
py_map_elem_t* py_map_lookup(py_obj_t o, py_obj_t index, bool add_if_not_found) {
98+
assert(IS_O(o, O_MAP));
99+
return py_map_lookup_helper(&((py_obj_base_t *)o)->u_map, index, add_if_not_found);
100+
}
101+
102+
py_obj_t py_set_lookup(py_obj_t o_in, py_obj_t index, bool add_if_not_found) {
103+
assert(IS_O(o_in, O_SET));
104+
py_obj_base_t *o = o_in;
105+
int hash = py_obj_hash(index);
106+
int pos = hash % o->u_set.alloc;
107+
for (;;) {
108+
py_obj_t elem = o->u_set.table[pos];
109+
if (elem == NULL) {
110+
// not in table
111+
if (add_if_not_found) {
112+
if (o->u_set.used + 1 >= o->u_set.alloc) {
113+
// not enough room in table, rehash it
114+
int old_alloc = o->u_set.alloc;
115+
py_obj_t *old_table = o->u_set.table;
116+
o->u_set.alloc = get_doubling_prime_greater_or_equal_to(o->u_set.alloc + 1);
117+
o->u_set.used = 0;
118+
o->u_set.table = m_new(py_obj_t, o->u_set.alloc);
119+
for (int i = 0; i < old_alloc; i++) {
120+
if (old_table[i] != NULL) {
121+
py_set_lookup(o, old_table[i], true);
122+
}
123+
}
124+
m_free(old_table);
125+
// restart the search for the new element
126+
pos = hash % o->u_set.alloc;
127+
} else {
128+
o->u_set.used += 1;
129+
o->u_set.table[pos] = index;
130+
return index;
131+
}
132+
} else {
133+
return NULL;
134+
}
135+
} else if (py_obj_equal(elem, index)) {
136+
// found it
137+
return elem;
138+
} else {
139+
// not yet found, keep searching in this table
140+
pos = (pos + 1) % o->u_set.alloc;
141+
}
142+
}
143+
}

py/map.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
typedef enum {
2+
MAP_QSTR,
3+
MAP_PY_OBJ,
4+
} py_map_kind_t;
5+
6+
typedef struct _py_map_elem_t {
7+
py_obj_t key;
8+
py_obj_t value;
9+
} py_map_elem_t;
10+
11+
typedef struct _py_map_t {
12+
struct {
13+
py_map_kind_t kind : 1;
14+
machine_uint_t used : (8 * BYTES_PER_WORD - 1);
15+
};
16+
machine_uint_t alloc;
17+
py_map_elem_t *table;
18+
} py_map_t;
19+
20+
// these are defined in runtime.c
21+
py_map_t *rt_get_map_locals(void);
22+
void rt_set_map_locals(py_map_t *m);
23+
24+
int get_doubling_prime_greater_or_equal_to(int x);
25+
void py_map_init(py_map_t *map, py_map_kind_t kind, int n);
26+
py_map_t *py_map_new(py_map_kind_t kind, int n);
27+
py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_not_found);
28+
py_map_elem_t* py_qstr_map_lookup(py_map_t *map, qstr index, bool add_if_not_found);
29+
py_map_elem_t* py_map_lookup(py_obj_t o, py_obj_t index, bool add_if_not_found);
30+
py_obj_t py_set_lookup(py_obj_t o_in, py_obj_t index, bool add_if_not_found);

0 commit comments

Comments
 (0)
0