Skip to content

Commit df94b71

Browse files
committed
modstruct: Implement count specifier for strings (e.g. "100s").
Infra for counts of other types is there, need last mile to be implemented.
1 parent 147c80b commit df94b71

2 files changed

Lines changed: 80 additions & 6 deletions

File tree

py/modstruct.c

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "objtuple.h"
3535
#include "objstr.h"
3636
#include "binary.h"
37+
#include "parsenum.h"
3738

3839
#if MICROPY_ENABLE_MOD_STRUCT
3940

@@ -56,9 +57,26 @@ STATIC char get_fmt_type(const char **fmt) {
5657
return t;
5758
}
5859

60+
STATIC machine_uint_t get_fmt_num(const char **p) {
61+
const char *num = *p;
62+
uint len = 1;
63+
while (unichar_isdigit(*++num)) {
64+
len++;
65+
}
66+
machine_uint_t val = (machine_uint_t)MP_OBJ_SMALL_INT_VALUE(mp_parse_num_integer(*p, len, 10));
67+
*p = num;
68+
return val;
69+
}
70+
5971
STATIC uint calcsize_items(const char *fmt) {
60-
// TODO
61-
return strlen(fmt);
72+
uint cnt = 0;
73+
while (*fmt) {
74+
// TODO supports size spec only for "s"
75+
if (!unichar_isdigit(*fmt++)) {
76+
cnt++;
77+
}
78+
}
79+
return cnt;
6280
}
6381

6482
STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
@@ -67,9 +85,23 @@ STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
6785
machine_uint_t size;
6886
for (size = 0; *fmt; fmt++) {
6987
uint align;
70-
int sz = mp_binary_get_size(fmt_type, *fmt, &align);
88+
machine_uint_t cnt = 1;
89+
if (unichar_isdigit(*fmt)) {
90+
cnt = get_fmt_num(&fmt);
91+
}
92+
if (cnt > 1) {
93+
// TODO: count spec support only for string len
94+
assert(*fmt == 's');
95+
}
96+
97+
machine_uint_t sz;
98+
if (*fmt == 's') {
99+
sz = cnt;
100+
} else {
101+
sz = (machine_uint_t)mp_binary_get_size(fmt_type, *fmt, &align);
102+
}
71103
// TODO
72-
assert(sz != -1);
104+
assert(sz != (machine_uint_t)-1);
73105
// Apply alignment
74106
size = (size + align - 1) & ~(align - 1);
75107
size += sz;
@@ -89,7 +121,22 @@ STATIC mp_obj_t struct_unpack(mp_obj_t fmt_in, mp_obj_t data_in) {
89121
byte *p = bufinfo.buf;
90122

91123
for (uint i = 0; i < size; i++) {
92-
mp_obj_t item = mp_binary_get_val(fmt_type, *fmt++, &p);
124+
machine_uint_t sz = 1;
125+
if (unichar_isdigit(*fmt)) {
126+
sz = get_fmt_num(&fmt);
127+
}
128+
if (sz > 1) {
129+
// TODO: size spec support only for string len
130+
assert(*fmt == 's');
131+
}
132+
mp_obj_t item;
133+
if (*fmt == 's') {
134+
item = mp_obj_new_bytes(p, sz);
135+
p += sz;
136+
fmt++;
137+
} else {
138+
item = mp_binary_get_val(fmt_type, *fmt++, &p);
139+
}
93140
res->items[i] = item;
94141
}
95142
return res;
@@ -106,7 +153,29 @@ STATIC mp_obj_t struct_pack(uint n_args, mp_obj_t *args) {
106153
memset(p, 0, size);
107154

108155
for (uint i = 1; i < n_args; i++) {
109-
mp_binary_set_val(fmt_type, *fmt++, args[i], &p);
156+
machine_uint_t sz = 1;
157+
if (unichar_isdigit(*fmt)) {
158+
sz = get_fmt_num(&fmt);
159+
}
160+
if (sz > 1) {
161+
// TODO: size spec support only for string len
162+
assert(*fmt == 's');
163+
}
164+
165+
if (*fmt == 's') {
166+
mp_buffer_info_t bufinfo;
167+
mp_get_buffer_raise(args[i], &bufinfo, MP_BUFFER_READ);
168+
machine_uint_t to_copy = sz;
169+
if (bufinfo.len < to_copy) {
170+
to_copy = bufinfo.len;
171+
}
172+
memcpy(p, bufinfo.buf, to_copy);
173+
memset(p + to_copy, 0, sz - to_copy);
174+
p += sz;
175+
fmt++;
176+
} else {
177+
mp_binary_set_val(fmt_type, *fmt++, args[i], &p);
178+
}
110179
}
111180
return res;
112181
}

tests/basics/struct1.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@
1616

1717
print(struct.pack("<bI", -128, 256))
1818
print(struct.pack(">bI", -128, 256))
19+
20+
print(struct.calcsize("100sI"))
21+
print(struct.calcsize("97sI"))
22+
print(struct.unpack("<6sH", b"foo\0\0\0\x12\x34"))
23+
print(struct.pack("<6sH", b"foo", 10000))

0 commit comments

Comments
 (0)