Skip to content

Commit 1022f9c

Browse files
spon-wwdpgeorge
authored andcommitted
py/modstruct: Fix struct.unpack with unaligned offset of native type.
With this patch alignment is done relative to the start of the buffer that is being unpacked, not the raw pointer value, as per CPython. Fixes issue adafruit#3314.
1 parent 12f13ee commit 1022f9c

5 files changed

Lines changed: 24 additions & 6 deletions

File tree

extmod/moductypes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, ucty
299299
static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
300300
char struct_type = big_endian ? '>' : '<';
301301
static const char type2char[16] = "BbHhIiQq------fd";
302-
return mp_binary_get_val(struct_type, type2char[val_type], &p);
302+
return mp_binary_get_val(struct_type, type2char[val_type], p, &p);
303303
}
304304

305305
static inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) {

py/binary.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,14 @@ long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, con
185185
}
186186

187187
#define is_signed(typecode) (typecode > 'Z')
188-
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) {
188+
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr) {
189189
byte *p = *ptr;
190190
mp_uint_t align;
191191

192192
size_t size = mp_binary_get_size(struct_type, val_type, &align);
193193
if (struct_type == '@') {
194-
// Make pointer aligned
195-
p = (byte*)MP_ALIGN(p, (size_t)align);
194+
// Align p relative to p_base
195+
p = p_base + (uintptr_t)MP_ALIGN(p - p_base, (size_t)align);
196196
#if MP_ENDIANNESS_LITTLE
197197
struct_type = '<';
198198
#else

py/binary.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign);
3838
mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index);
3939
void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t val_in);
4040
void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, mp_int_t val);
41-
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr);
41+
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr);
4242
void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr);
4343
long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, const byte *src);
4444
void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t val);

py/modstruct.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) {
146146
}
147147
p += offset;
148148
}
149+
byte *p_base = p;
149150

150151
// Check that the input buffer is big enough to unpack all the values
151152
if (p + total_sz > end_p) {
@@ -164,7 +165,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) {
164165
res->items[i++] = item;
165166
} else {
166167
while (cnt--) {
167-
item = mp_binary_get_val(fmt_type, *fmt, &p);
168+
item = mp_binary_get_val(fmt_type, *fmt, p_base, &p);
168169
res->items[i++] = item;
169170
}
170171
}

tests/basics/struct_endian.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# test ustruct and endian specific things
2+
3+
try:
4+
import ustruct as struct
5+
except:
6+
try:
7+
import struct
8+
except ImportError:
9+
print("SKIP")
10+
raise SystemExit
11+
12+
# unpack/unpack_from with unaligned native type
13+
buf = b'0123456789'
14+
print(struct.unpack('h', memoryview(buf)[1:3]))
15+
print(struct.unpack_from('i', buf, 1))
16+
print(struct.unpack_from('@i', buf, 1))
17+
print(struct.unpack_from('@ii', buf, 1))

0 commit comments

Comments
 (0)