Skip to content

Commit 8215847

Browse files
committed
moductypes: Foreign data interface module, roughly based on ctype ideas.
But much smaller and memory-efficient. Uses Python builtin data structures (dict, tuple, int) to describe structure layout.
1 parent 42b6419 commit 8215847

18 files changed

Lines changed: 974 additions & 0 deletions

extmod/moductypes.c

Lines changed: 630 additions & 0 deletions
Large diffs are not rendered by default.

py/builtin.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,6 @@ extern const mp_obj_module_t mp_module_micropython;
8080
extern const mp_obj_module_t mp_module_struct;
8181
extern const mp_obj_module_t mp_module_sys;
8282
extern const mp_obj_module_t mp_module_gc;
83+
84+
// extmod modules
85+
extern const mp_obj_module_t mp_module_uctypes;

py/builtintables.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ STATIC const mp_map_elem_t mp_builtin_module_table[] = {
190190
{ MP_OBJ_NEW_QSTR(MP_QSTR_gc), (mp_obj_t)&mp_module_gc },
191191
#endif
192192

193+
// extmod modules
194+
195+
#if MICROPY_PY_UCTYPES
196+
{ MP_OBJ_NEW_QSTR(MP_QSTR_uctypes), (mp_obj_t)&mp_module_uctypes },
197+
#endif
198+
193199
// extra builtin modules as defined by a port
194200
MICROPY_PORT_BUILTIN_MODULES
195201
};

py/compile.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ STATIC void compile_syntax_error(compiler_t *comp, mp_parse_node_t pn, const cha
104104
}
105105

106106
STATIC const mp_map_elem_t mp_constants_table[] = {
107+
#if MICROPY_PY_UCTYPES
108+
{ MP_OBJ_NEW_QSTR(MP_QSTR_uctypes), (mp_obj_t)&mp_module_uctypes },
109+
#endif
107110
// Extra constants as defined by a port
108111
MICROPY_PORT_CONSTANTS
109112
};

py/mpconfig.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,12 @@ typedef double mp_float_t;
351351
#define MICROPY_PY_SYS_STDFILES (0)
352352
#endif
353353

354+
355+
// Extended modules
356+
#ifndef MICROPY_PY_UCTYPES
357+
#define MICROPY_PY_UCTYPES (0)
358+
#endif
359+
354360
/*****************************************************************************/
355361
/* Hooks for a port to add builtins */
356362

py/py.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ PY_O_BASENAME = \
102102
repl.o \
103103
smallint.o \
104104
pfenv.o \
105+
../extmod/moductypes.o
105106

106107
# prepend the build destination prefix to the py object files
107108
PY_O = $(addprefix $(PY_BUILD)/, $(PY_O_BASENAME))

py/qstrdefs.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,43 @@ Q(pack)
363363
Q(unpack)
364364
#endif
365365

366+
#if MICROPY_PY_UCTYPES
367+
Q(uctypes)
368+
Q(sizeof)
369+
Q(addressof)
370+
Q(bytes_at)
371+
Q(bytearray_at)
372+
373+
Q(NATIVE)
374+
Q(LITTLE_ENDIAN)
375+
Q(BIG_ENDIAN)
376+
377+
Q(VOID)
378+
379+
Q(UINT8)
380+
Q(INT8)
381+
Q(UINT16)
382+
Q(INT16)
383+
Q(UINT32)
384+
Q(INT32)
385+
Q(UINT64)
386+
Q(INT64)
387+
388+
Q(BFUINT8)
389+
Q(BFINT8)
390+
Q(BFUINT16)
391+
Q(BFINT16)
392+
Q(BFUINT32)
393+
Q(BFINT32)
394+
395+
Q(FLOAT32)
396+
Q(FLOAT64)
397+
398+
Q(ARRAY)
399+
Q(PTR)
400+
//Q(BITFIELD)
401+
#endif
402+
366403
#if MICROPY_PY_IO
367404
Q(_io)
368405
Q(readall)

tests/extmod/uctypes_le.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import uctypes
2+
3+
desc = {
4+
"s0": uctypes.UINT16 | 0,
5+
"sub": (0, {
6+
"b0": uctypes.UINT8 | 0,
7+
"b1": uctypes.UINT8 | 1,
8+
}),
9+
"arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2),
10+
"arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}),
11+
"bitf0": uctypes.BFUINT16 | 0 | 0 << 17 | 8 << 22,
12+
"bitf1": uctypes.BFUINT16 | 0 | 8 << 17 | 8 << 22,
13+
14+
"bf0": uctypes.BFUINT16 | 0 | 0 << 17 | 4 << 22,
15+
"bf1": uctypes.BFUINT16 | 0 | 4 << 17 | 4 << 22,
16+
"bf2": uctypes.BFUINT16 | 0 | 8 << 17 | 4 << 22,
17+
"bf3": uctypes.BFUINT16 | 0 | 12 << 17 | 4 << 22,
18+
19+
"ptr": (uctypes.PTR | 0, uctypes.UINT8),
20+
"ptr2": (uctypes.PTR | 0, {"b": uctypes.UINT8 | 0}),
21+
}
22+
23+
data = bytearray(b"01")
24+
25+
S = uctypes.struct(desc, uctypes.addressof(data), uctypes.LITTLE_ENDIAN)
26+
27+
#print(S)
28+
print(hex(S.s0))
29+
assert hex(S.s0) == "0x3130"
30+
#print(S.sub.b0)
31+
print(S.sub.b0, S.sub.b1)
32+
assert S.sub.b0, S.sub.b1 == (0x30, 0x31)
33+
34+
try:
35+
S[0]
36+
assert False, "Can't index struct"
37+
except TypeError:
38+
print("TypeError")
39+
40+
print("arr:", S.arr[0], S.arr[1])
41+
assert (S.arr[0], S.arr[1]) == (0x30, 0x31)
42+
43+
print("arr of struct:", S.arr2[0].b, S.arr2[1].b)
44+
assert (S.arr2[0].b, S.arr2[1].b) == (0x30, 0x31)
45+
46+
47+
try:
48+
S.arr[2]
49+
assert False, "Out of bounds index"
50+
except IndexError:
51+
print("IndexError")
52+
53+
print("bf:", S.bitf0, S.bitf1)
54+
assert (S.bitf0, S.bitf1) == (0x30, 0x31)
55+
56+
print("bf 4bit:", S.bf3, S.bf2, S.bf1, S.bf0)
57+
assert (S.bf3, S.bf2, S.bf1, S.bf0) == (3, 1, 3, 0)
58+
59+
60+
# Write access
61+
62+
S.sub.b0 = ord("2")
63+
print(data)
64+
assert bytes(data) == b"21"
65+
66+
S.bf3 = 5
67+
print(data)
68+
assert bytes(data) == b"2Q"

tests/extmod/uctypes_le.py.exp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
0x3130
2+
48 49
3+
TypeError
4+
arr: 48 49
5+
arr of struct: 48 49
6+
IndexError
7+
bf: 48 49
8+
bf 4bit: 3 1 3 0
9+
bytearray(b'21')
10+
bytearray(b'2Q')

tests/extmod/uctypes_native_le.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# This test is exactly like uctypes_le.py, but uses native structure layout.
2+
# Codepaths for packed vs native structures are different. This test only works
3+
# on little-endian machine (no matter if 32 or 64 bit).
4+
import sys
5+
import uctypes
6+
7+
if sys.byteorder != "little":
8+
print("SKIP")
9+
sys.exit()
10+
11+
12+
desc = {
13+
"s0": uctypes.UINT16 | 0,
14+
"sub": (0, {
15+
"b0": uctypes.UINT8 | 0,
16+
"b1": uctypes.UINT8 | 1,
17+
}),
18+
"arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2),
19+
"arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}),
20+
"bitf0": uctypes.BFUINT16 | 0 | 0 << 17 | 8 << 22,
21+
"bitf1": uctypes.BFUINT16 | 0 | 8 << 17 | 8 << 22,
22+
23+
"bf0": uctypes.BFUINT16 | 0 | 0 << 17 | 4 << 22,
24+
"bf1": uctypes.BFUINT16 | 0 | 4 << 17 | 4 << 22,
25+
"bf2": uctypes.BFUINT16 | 0 | 8 << 17 | 4 << 22,
26+
"bf3": uctypes.BFUINT16 | 0 | 12 << 17 | 4 << 22,
27+
28+
"ptr": (uctypes.PTR | 0, uctypes.UINT8),
29+
"ptr2": (uctypes.PTR | 0, {"b": uctypes.UINT8 | 0}),
30+
}
31+
32+
data = bytearray(b"01")
33+
34+
S = uctypes.struct(desc, uctypes.addressof(data), uctypes.NATIVE)
35+
36+
#print(S)
37+
print(hex(S.s0))
38+
assert hex(S.s0) == "0x3130"
39+
#print(S.sub.b0)
40+
print(S.sub.b0, S.sub.b1)
41+
assert S.sub.b0, S.sub.b1 == (0x30, 0x31)
42+
43+
try:
44+
S[0]
45+
assert False, "Can't index struct"
46+
except TypeError:
47+
print("TypeError")
48+
49+
print("arr:", S.arr[0], S.arr[1])
50+
assert (S.arr[0], S.arr[1]) == (0x30, 0x31)
51+
52+
print("arr of struct:", S.arr2[0].b, S.arr2[1].b)
53+
assert (S.arr2[0].b, S.arr2[1].b) == (0x30, 0x31)
54+
55+
56+
try:
57+
S.arr[2]
58+
assert False, "Out of bounds index"
59+
except IndexError:
60+
print("IndexError")
61+
62+
print("bf:", S.bitf0, S.bitf1)
63+
assert (S.bitf0, S.bitf1) == (0x30, 0x31)
64+
65+
print("bf 4bit:", S.bf3, S.bf2, S.bf1, S.bf0)
66+
assert (S.bf3, S.bf2, S.bf1, S.bf0) == (3, 1, 3, 0)
67+
68+
# Write access
69+
70+
S.sub.b0 = ord("2")
71+
print(data)
72+
assert bytes(data) == b"21"
73+
74+
S.bf3 = 5
75+
print(data)
76+
assert bytes(data) == b"2Q"

0 commit comments

Comments
 (0)