moductypes.c 27.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * This file is part of the Micro Python project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Paul Sokolovsky
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <assert.h>
#include <string.h>
#include <stdint.h>
30
31
32
33
34

#include "py/nlr.h"
#include "py/runtime.h"
#include "py/objtuple.h"
#include "py/binary.h"
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

#if MICROPY_PY_UCTYPES

/// \module uctypes - Access data structures in memory
///
/// The module allows to define layout of raw data structure (using terms
/// of C language), and then access memory buffers using this definition.
/// The module also provides convenience functions to access memory buffers
/// contained in Python objects or wrap memory buffers in Python objects.
/// \constant UINT8_1 - uint8_t value type

/// \class struct - C-like structure
///
/// Encapsulalation of in-memory data structure. This class doesn't define
/// any methods, only attribute access (for structure fields) and
/// indexing (for pointer and array fields).
///
/// Usage:
///
///     # Define layout of a structure with 2 fields
///     # 0 and 4 are byte offsets of fields from the beginning of struct
///     # they are logically ORed with field type
///     FOO_STRUCT = {"a": 0 | uctypes.UINT32, "b": 4 | uctypes.UINT8}
///
///     # Example memory buffer to access (contained in bytes object)
///     buf = b"\x64\0\0\0\0x14"
///
///     # Create structure object referring to address of
///     # the data in the buffer above
///     s = uctypes.struct(FOO_STRUCT, uctypes.addressof(buf))
///
///     # Access fields
///     print(s.a, s.b)
///     # Result:
///     # 100, 20

#define LAYOUT_LITTLE_ENDIAN (0)
#define LAYOUT_BIG_ENDIAN    (1)
#define LAYOUT_NATIVE        (2)

#define VAL_TYPE_BITS 4
#define BITF_LEN_BITS 5
#define BITF_OFF_BITS 5
#define OFFSET_BITS 17
#if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 31
#error Invalid encoding field length
#endif

enum {
    UINT8, INT8, UINT16, INT16,
    UINT32, INT32, UINT64, INT64,

    BFUINT8, BFINT8, BFUINT16, BFINT16,
    BFUINT32, BFINT32,

    FLOAT32, FLOAT64,
};

#define AGG_TYPE_BITS 2

enum {
    STRUCT, PTR, ARRAY, BITFIELD,
};

// Here we need to set sign bit right
#define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 1)
101
#define GET_TYPE(x, nbits) (((x) >> (31 - nbits)) & ((1 << nbits) - 1))
102
103
104
105
// Bit 0 is "is_signed"
#define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1))
#define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits)

106
107
108
109
#define IS_SCALAR_ARRAY(tuple_desc) ((tuple_desc)->len == 2)
// We cannot apply the below to INT8, as their range [-128, 127]
#define IS_SCALAR_ARRAY_OF_BYTES(tuple_desc) (GET_TYPE(MP_OBJ_SMALL_INT_VALUE((tuple_desc)->items[1]), VAL_TYPE_BITS) == UINT8)

110
// "struct" in uctypes context means "structural", i.e. aggregate, type.
111
112
113
114
115
116
117
118
119
STATIC const mp_obj_type_t uctypes_struct_type;

typedef struct _mp_obj_uctypes_struct_t {
    mp_obj_base_t base;
    mp_obj_t desc;
    byte *addr;
    uint32_t flags;
} mp_obj_uctypes_struct_t;

120
STATIC NORETURN void syntax_error(void) {
121
122
123
    nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "syntax error in uctypes descriptor"));
}

124
STATIC mp_obj_t uctypes_struct_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
125
    mp_arg_check_num(n_args, n_kw, 2, 3, false);
126
    mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
127
    o->base.type = type;
128
    o->addr = (void*)(uintptr_t)mp_obj_get_int(args[0]);
129
    o->desc = args[1];
130
131
132
133
    o->flags = LAYOUT_NATIVE;
    if (n_args == 3) {
        o->flags = mp_obj_get_int(args[2]);
    }
134
    return MP_OBJ_FROM_PTR(o);
135
136
}

137
STATIC void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
138
    (void)kind;
139
    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
140
141
142
143
    const char *typen = "unk";
    if (MP_OBJ_IS_TYPE(self->desc, &mp_type_dict)) {
        typen = "STRUCT";
    } else if (MP_OBJ_IS_TYPE(self->desc, &mp_type_tuple)) {
144
        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
145
146
147
148
149
150
151
152
153
        mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
        uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
        switch (agg_type) {
            case PTR: typen = "PTR"; break;
            case ARRAY: typen = "ARRAY"; break;
        }
    } else {
        typen = "ERROR";
    }
154
    mp_printf(print, "<struct %s %p>", typen, self->addr);
155
156
}

157
// Get size of any type descriptor
158
STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size);
159
160

// Get size of scalar type descriptor
161
162
163
164
165
166
167
168
static inline mp_uint_t uctypes_struct_scalar_size(int val_type) {
    if (val_type == FLOAT32) {
        return 4;
    } else {
        return GET_SCALAR_SIZE(val_type & 7);
    }
}

169
// Get size of aggregate type descriptor
170
STATIC mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_uint_t *max_field_size) {
171
172
173
174
175
176
177
    mp_uint_t total_size = 0;

    mp_int_t offset_ = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
    mp_uint_t agg_type = GET_TYPE(offset_, AGG_TYPE_BITS);

    switch (agg_type) {
        case STRUCT:
178
            return uctypes_struct_size(t->items[1], layout_type, max_field_size);
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
        case PTR:
            if (sizeof(void*) > *max_field_size) {
                *max_field_size = sizeof(void*);
            }
            return sizeof(void*);
        case ARRAY: {
            mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]);
            uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS);
            arr_sz &= VALUE_MASK(VAL_TYPE_BITS);
            mp_uint_t item_s;
            if (t->len == 2) {
                // Elements of array are scalar
                item_s = GET_SCALAR_SIZE(val_type);
                if (item_s > *max_field_size) {
                    *max_field_size = item_s;
                }
            } else {
                // Elements of array are aggregates
197
                item_s = uctypes_struct_size(t->items[2], layout_type, max_field_size);
198
199
200
201
202
203
204
205
206
207
208
            }

            return item_s * arr_sz;
        }
        default:
            assert(0);
    }

    return total_size;
}

209
STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size) {
210
    if (!MP_OBJ_IS_TYPE(desc_in, &mp_type_dict)) {
211
        if (MP_OBJ_IS_TYPE(desc_in, &mp_type_tuple)) {
212
            return uctypes_struct_agg_size((mp_obj_tuple_t*)MP_OBJ_TO_PTR(desc_in), layout_type, max_field_size);
213
214
215
216
217
218
219
        } else if (MP_OBJ_IS_SMALL_INT(desc_in)) {
            // We allow sizeof on both type definitions and structures/structure fields,
            // but scalar structure field is lowered into native Python int, so all
            // type info is lost. So, we cannot say if it's scalar type description,
            // or such lowered scalar.
            nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "Cannot unambiguously get sizeof scalar"));
        }
220
221
222
        syntax_error();
    }

223
224
225
    mp_obj_dict_t *d = MP_OBJ_TO_PTR(desc_in);
    mp_uint_t total_size = 0;

226
227
228
229
230
231
232
    for (mp_uint_t i = 0; i < d->map.alloc; i++) {
        if (MP_MAP_SLOT_IS_FILLED(&d->map, i)) {
            mp_obj_t v = d->map.table[i].value;
            if (MP_OBJ_IS_SMALL_INT(v)) {
                mp_uint_t offset = MP_OBJ_SMALL_INT_VALUE(v);
                mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS);
                offset &= VALUE_MASK(VAL_TYPE_BITS);
233
234
235
                if (val_type >= BFUINT8 && val_type <= BFINT32) {
                    offset &= (1 << OFFSET_BITS) - 1;
                }
236
237
238
239
240
241
242
243
244
245
246
                mp_uint_t s = uctypes_struct_scalar_size(val_type);
                if (s > *max_field_size) {
                    *max_field_size = s;
                }
                if (offset + s > total_size) {
                    total_size = offset + s;
                }
            } else {
                if (!MP_OBJ_IS_TYPE(v, &mp_type_tuple)) {
                    syntax_error();
                }
247
                mp_obj_tuple_t *t = MP_OBJ_TO_PTR(v);
248
249
                mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
                offset &= VALUE_MASK(AGG_TYPE_BITS);
250
                mp_uint_t s = uctypes_struct_agg_size(t, layout_type, max_field_size);
251
252
                if (offset + s > total_size) {
                    total_size = offset + s;
253
254
255
256
257
258
                }
            }
        }
    }

    // Round size up to alignment of biggest field
259
260
261
    if (layout_type == LAYOUT_NATIVE) {
        total_size = (total_size + *max_field_size - 1) & ~(*max_field_size - 1);
    }
262
263
264
265
266
    return total_size;
}

STATIC mp_obj_t uctypes_struct_sizeof(mp_obj_t obj_in) {
    mp_uint_t max_field_size = 0;
267
268
269
    if (MP_OBJ_IS_TYPE(obj_in, &mp_type_bytearray)) {
        return mp_obj_len(obj_in);
    }
270
    int layout_type = LAYOUT_NATIVE;
271
272
    // We can apply sizeof either to structure definition (a dict)
    // or to instantiated structure
273
    if (MP_OBJ_IS_TYPE(obj_in, &uctypes_struct_type)) {
274
        // Extract structure definition
275
        mp_obj_uctypes_struct_t *obj = MP_OBJ_TO_PTR(obj_in);
276
        obj_in = obj->desc;
277
        layout_type = obj->flags;
278
    }
279
    mp_uint_t size = uctypes_struct_size(obj_in, layout_type, &max_field_size);
280
281
282
283
    return MP_OBJ_NEW_SMALL_INT(size);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_sizeof_obj, uctypes_struct_sizeof);

284
STATIC inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
285
    char struct_type = big_endian ? '>' : '<';
286
    static const char type2char[16] = "BbHhIiQq------fd";
287
    return mp_binary_get_val(struct_type, type2char[val_type], &p);
288
289
}

290
STATIC inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) {
291
    char struct_type = big_endian ? '>' : '<';
292
    static const char type2char[16] = "BbHhIiQq------fd";
293
    mp_binary_set_val(struct_type, type2char[val_type], val, &p);
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
}

static inline mp_uint_t get_aligned_basic(uint val_type, void *p) {
    switch (val_type) {
        case UINT8:
            return *(uint8_t*)p;
        case UINT16:
            return *(uint16_t*)p;
        case UINT32:
            return *(uint32_t*)p;
    }
    assert(0);
    return 0;
}

static inline void set_aligned_basic(uint val_type, void *p, mp_uint_t v) {
    switch (val_type) {
        case UINT8:
            *(uint8_t*)p = (uint8_t)v; return;
        case UINT16:
            *(uint16_t*)p = (uint16_t)v; return;
        case UINT32:
            *(uint32_t*)p = (uint32_t)v; return;
    }
    assert(0);
}

STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
    switch (val_type) {
        case UINT8:
324
            return MP_OBJ_NEW_SMALL_INT(((uint8_t*)p)[index]);
325
        case INT8:
326
            return MP_OBJ_NEW_SMALL_INT(((int8_t*)p)[index]);
327
        case UINT16:
328
            return MP_OBJ_NEW_SMALL_INT(((uint16_t*)p)[index]);
329
        case INT16:
330
            return MP_OBJ_NEW_SMALL_INT(((int16_t*)p)[index]);
331
332
333
334
335
        case UINT32:
            return mp_obj_new_int_from_uint(((uint32_t*)p)[index]);
        case INT32:
            return mp_obj_new_int(((int32_t*)p)[index]);
        case UINT64:
336
            return mp_obj_new_int_from_ull(((uint64_t*)p)[index]);
337
338
        case INT64:
            return mp_obj_new_int_from_ll(((int64_t*)p)[index]);
Dave Hylands's avatar
Dave Hylands committed
339
        #if MICROPY_PY_BUILTINS_FLOAT
340
341
342
343
        case FLOAT32:
            return mp_obj_new_float(((float*)p)[index]);
        case FLOAT64:
            return mp_obj_new_float(((double*)p)[index]);
Dave Hylands's avatar
Dave Hylands committed
344
        #endif
345
346
        default:
            assert(0);
347
            return MP_OBJ_NULL;
348
349
350
351
    }
}

STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
352
353
354
355
356
357
358
359
360
361
362
    #if MICROPY_PY_BUILTINS_FLOAT
    if (val_type == FLOAT32 || val_type == FLOAT64) {
        mp_float_t v = mp_obj_get_float(val);
        if (val_type == FLOAT32) {
            ((float*)p)[index] = v;
        } else {
            ((double*)p)[index] = v;
        }
        return;
    }
    #endif
363
364
365
366
367
368
369
370
371
372
373
374
375
376
    mp_int_t v = mp_obj_get_int(val);
    switch (val_type) {
        case UINT8:
            ((uint8_t*)p)[index] = (uint8_t)v; return;
        case INT8:
            ((int8_t*)p)[index] = (int8_t)v; return;
        case UINT16:
            ((uint16_t*)p)[index] = (uint16_t)v; return;
        case INT16:
            ((int16_t*)p)[index] = (int16_t)v; return;
        case UINT32:
            ((uint32_t*)p)[index] = (uint32_t)v; return;
        case INT32:
            ((int32_t*)p)[index] = (int32_t)v; return;
377
378
379
380
381
382
383
384
385
        case INT64:
        case UINT64:
            if (sizeof(mp_int_t) == 8) {
                ((uint64_t*)p)[index] = (uint64_t)v;
            } else {
                // TODO: Doesn't offer atomic store semantics, but should at least try
                set_unaligned(val_type, p, MP_ENDIANNESS_BIG, val);
            }
            return;
386
387
388
389
390
391
        default:
            assert(0);
    }
}

STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val) {
392
    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
393
394
395
396
397
398
399
400
401
402
403
404
405

    // TODO: Support at least OrderedDict in addition
    if (!MP_OBJ_IS_TYPE(self->desc, &mp_type_dict)) {
            nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "struct: no fields"));
    }

    mp_obj_t deref = mp_obj_dict_get(self->desc, MP_OBJ_NEW_QSTR(attr));
    if (MP_OBJ_IS_SMALL_INT(deref)) {
        mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(deref);
        mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS);
        offset &= VALUE_MASK(VAL_TYPE_BITS);
//printf("scalar type=%d offset=%x\n", val_type, offset);

406
        if (val_type <= INT64 || val_type == FLOAT32 || val_type == FLOAT64) {
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
//            printf("size=%d\n", GET_SCALAR_SIZE(val_type));
            if (self->flags == LAYOUT_NATIVE) {
                if (set_val == MP_OBJ_NULL) {
                    return get_aligned(val_type, self->addr + offset, 0);
                } else {
                    set_aligned(val_type, self->addr + offset, 0, set_val);
                    return set_val; // just !MP_OBJ_NULL
                }
            } else {
                if (set_val == MP_OBJ_NULL) {
                    return get_unaligned(val_type, self->addr + offset, self->flags);
                } else {
                    set_unaligned(val_type, self->addr + offset, self->flags, set_val);
                    return set_val; // just !MP_OBJ_NULL
                }
            }
        } else if (val_type >= BFUINT8 && val_type <= BFINT32) {
            uint bit_offset = (offset >> 17) & 31;
            uint bit_len = (offset >> 22) & 31;
            offset &= (1 << 17) - 1;
            mp_uint_t val;
            if (self->flags == LAYOUT_NATIVE) {
                val = get_aligned_basic(val_type & 6, self->addr + offset);
            } else {
                val = mp_binary_get_int(GET_SCALAR_SIZE(val_type & 7), val_type & 1, self->flags, self->addr + offset);
            }
            if (set_val == MP_OBJ_NULL) {
                val >>= bit_offset;
                val &= (1 << bit_len) - 1;
                // TODO: signed
                assert((val_type & 1) == 0);
                return mp_obj_new_int(val);
            } else {
                mp_uint_t set_val_int = (mp_uint_t)mp_obj_get_int(set_val);
                mp_uint_t mask = (1 << bit_len) - 1;
                set_val_int &= mask;
                set_val_int <<= bit_offset;
                mask <<= bit_offset;
                val = (val & ~mask) | set_val_int;

                if (self->flags == LAYOUT_NATIVE) {
                    set_aligned_basic(val_type & 6, self->addr + offset, val);
                } else {
                    mp_binary_set_int(GET_SCALAR_SIZE(val_type & 7), self->flags == LAYOUT_BIG_ENDIAN,
451
                        self->addr + offset, val);
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
                }
                return set_val; // just !MP_OBJ_NULL
            }
        }

        assert(0);
        return MP_OBJ_NULL;
    }

    if (!MP_OBJ_IS_TYPE(deref, &mp_type_tuple)) {
        syntax_error();
    }

    if (set_val != MP_OBJ_NULL) {
        // Cannot assign to aggregate
        syntax_error();
    }

470
    mp_obj_tuple_t *sub = MP_OBJ_TO_PTR(deref);
471
472
473
474
475
476
477
478
479
480
481
482
    mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[0]);
    mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
    offset &= VALUE_MASK(AGG_TYPE_BITS);
//printf("agg type=%d offset=%x\n", agg_type, offset);

    switch (agg_type) {
        case STRUCT: {
            mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
            o->base.type = &uctypes_struct_type;
            o->desc = sub->items[1];
            o->addr = self->addr + offset;
            o->flags = self->flags;
483
            return MP_OBJ_FROM_PTR(o);
484
        }
485
486
487
        case ARRAY: {
            mp_uint_t dummy;
            if (IS_SCALAR_ARRAY(sub) && IS_SCALAR_ARRAY_OF_BYTES(sub)) {
488
                return mp_obj_new_bytearray_by_ref(uctypes_struct_agg_size(sub, self->flags, &dummy), self->addr + offset);
489
490
491
492
            }
            // Fall thru to return uctypes struct object
        }
        case PTR: {
493
494
            mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
            o->base.type = &uctypes_struct_type;
495
            o->desc = MP_OBJ_FROM_PTR(sub);
496
497
498
            o->addr = self->addr + offset;
            o->flags = self->flags;
//printf("PTR/ARR base addr=%p\n", o->addr);
499
            return MP_OBJ_FROM_PTR(o);
500
501
502
503
504
505
506
        }
    }

    // Should be unreachable once all cases are handled
    return MP_OBJ_NULL;
}

507
508
509
510
511
512
513
514
515
516
517
STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
    if (dest[0] == MP_OBJ_NULL) {
        // load attribute
        mp_obj_t val = uctypes_struct_attr_op(self_in, attr, MP_OBJ_NULL);
        dest[0] = val;
    } else {
        // delete/store attribute
        if (uctypes_struct_attr_op(self_in, attr, dest[1]) != MP_OBJ_NULL) {
            dest[0] = MP_OBJ_NULL; // indicate success
        }
    }
518
519
520
}

STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
521
    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
522
523
524
525

    if (value == MP_OBJ_NULL) {
        // delete
        return MP_OBJ_NULL; // op not supported
526
527
    } else {
        // load / store
528
529
530
531
        if (!MP_OBJ_IS_TYPE(self->desc, &mp_type_tuple)) {
            nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "struct: cannot index"));
        }

532
        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc);
533
534
535
536
537
538
539
540
541
542
543
544
545
546
        mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
        uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);

        mp_int_t index = MP_OBJ_SMALL_INT_VALUE(index_in);

        if (agg_type == ARRAY) {
            mp_int_t arr_sz = MP_OBJ_SMALL_INT_VALUE(t->items[1]);
            uint val_type = GET_TYPE(arr_sz, VAL_TYPE_BITS);
            arr_sz &= VALUE_MASK(VAL_TYPE_BITS);
            if (index >= arr_sz) {
                nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "struct: index out of range"));
            }

            if (t->len == 2) {
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
                // array of scalars
                if (self->flags == LAYOUT_NATIVE) {
                    if (value == MP_OBJ_SENTINEL) {
                        return get_aligned(val_type, self->addr, index);
                    } else {
                        set_aligned(val_type, self->addr, index, value);
                        return value; // just !MP_OBJ_NULL
                    }
                } else {
                    byte *p = self->addr + GET_SCALAR_SIZE(val_type) * index;
                    if (value == MP_OBJ_SENTINEL) {
                        return get_unaligned(val_type, p, self->flags);
                    } else {
                        set_unaligned(val_type, p, self->flags, value);
                        return value; // just !MP_OBJ_NULL
                    }
                }
            } else if (value == MP_OBJ_SENTINEL) {
565
                mp_uint_t dummy = 0;
566
                mp_uint_t size = uctypes_struct_size(t->items[2], self->flags, &dummy);
567
568
569
570
571
                mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
                o->base.type = &uctypes_struct_type;
                o->desc = t->items[2];
                o->addr = self->addr + size * index;
                o->flags = self->flags;
572
                return MP_OBJ_FROM_PTR(o);
573
574
            } else {
                return MP_OBJ_NULL; // op not supported
575
            }
576

577
578
579
580
581
582
583
        } else if (agg_type == PTR) {
            byte *p = *(void**)self->addr;
            if (MP_OBJ_IS_SMALL_INT(t->items[1])) {
                uint val_type = GET_TYPE(MP_OBJ_SMALL_INT_VALUE(t->items[1]), VAL_TYPE_BITS);
                return get_aligned(val_type, p, index);
            } else {
                mp_uint_t dummy = 0;
584
                mp_uint_t size = uctypes_struct_size(t->items[1], self->flags, &dummy);
585
586
587
588
589
                mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t);
                o->base.type = &uctypes_struct_type;
                o->desc = t->items[1];
                o->addr = p + size * index;
                o->flags = self->flags;
590
                return MP_OBJ_FROM_PTR(o);
591
592
593
594
            }
        }

        assert(0);
595
        return MP_OBJ_NULL;
596
597
598
    }
}

599
600
STATIC mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
    (void)flags;
601
    mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
602
    mp_uint_t max_field_size = 0;
603
    mp_uint_t size = uctypes_struct_size(self->desc, self->flags, &max_field_size);
604
605
606
607
608
609
610

    bufinfo->buf = self->addr;
    bufinfo->len = size;
    bufinfo->typecode = BYTEARRAY_TYPECODE;
    return 0;
}

611
612
613
/// \function addressof()
/// Return address of object's data (applies to object providing buffer
/// interface).
614
STATIC mp_obj_t uctypes_struct_addressof(mp_obj_t buf) {
615
616
    mp_buffer_info_t bufinfo;
    mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
617
    return mp_obj_new_int((mp_int_t)(uintptr_t)bufinfo.buf);
618
619
620
621
622
623
624
}
MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_addressof_obj, uctypes_struct_addressof);

/// \function bytearray_at()
/// Capture memory at given address of given size as bytearray. Memory is
/// captured by reference (and thus memory pointed by bytearray may change
/// or become invalid at later time). Use bytes_at() to capture by value.
625
STATIC mp_obj_t uctypes_struct_bytearray_at(mp_obj_t ptr, mp_obj_t size) {
626
    return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void*)(uintptr_t)mp_obj_int_get_truncated(ptr));
627
628
629
630
631
632
633
}
MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytearray_at_obj, uctypes_struct_bytearray_at);

/// \function bytes_at()
/// Capture memory at given address of given size as bytes. Memory is
/// captured by value, i.e. copied. Use bytearray_at() to capture by reference
/// ("zero copy").
634
STATIC mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) {
635
    return mp_obj_new_bytes((void*)(uintptr_t)mp_obj_int_get_truncated(ptr), mp_obj_int_get_truncated(size));
636
637
638
639
640
641
642
643
644
}
MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at);


STATIC const mp_obj_type_t uctypes_struct_type = {
    { &mp_type_type },
    .name = MP_QSTR_struct,
    .print = uctypes_struct_print,
    .make_new = uctypes_struct_make_new,
645
    .attr = uctypes_struct_attr,
646
    .subscr = uctypes_struct_subscr,
647
    .buffer_p = { .get_buffer = uctypes_get_buffer },
648
649
};

650
651
652
653
654
655
656
STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uctypes) },
    { MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&uctypes_struct_type) },
    { MP_ROM_QSTR(MP_QSTR_sizeof), MP_ROM_PTR(&uctypes_struct_sizeof_obj) },
    { MP_ROM_QSTR(MP_QSTR_addressof), MP_ROM_PTR(&uctypes_struct_addressof_obj) },
    { MP_ROM_QSTR(MP_QSTR_bytes_at), MP_ROM_PTR(&uctypes_struct_bytes_at_obj) },
    { MP_ROM_QSTR(MP_QSTR_bytearray_at), MP_ROM_PTR(&uctypes_struct_bytearray_at_obj) },
657
658
659
660
661

    /// \moduleref uctypes

    /// \constant NATIVE - Native structure layout - native endianness,
    /// platform-specific field alignment
662
    { MP_ROM_QSTR(MP_QSTR_NATIVE), MP_ROM_INT(LAYOUT_NATIVE) },
663
664
    /// \constant LITTLE_ENDIAN - Little-endian structure layout, tightly packed
    /// (no alignment constraints)
665
    { MP_ROM_QSTR(MP_QSTR_LITTLE_ENDIAN), MP_ROM_INT(LAYOUT_LITTLE_ENDIAN) },
666
667
    /// \constant BIG_ENDIAN - Big-endian structure layout, tightly packed
    /// (no alignment constraints)
668
    { MP_ROM_QSTR(MP_QSTR_BIG_ENDIAN), MP_ROM_INT(LAYOUT_BIG_ENDIAN) },
669
670

    /// \constant VOID - void value type, may be used only as pointer target type.
671
    { MP_ROM_QSTR(MP_QSTR_VOID), MP_ROM_INT(TYPE2SMALLINT(UINT8, VAL_TYPE_BITS)) },
672
673

    /// \constant UINT8 - uint8_t value type
674
    { MP_ROM_QSTR(MP_QSTR_UINT8), MP_ROM_INT(TYPE2SMALLINT(UINT8, 4)) },
675
    /// \constant INT8 - int8_t value type
676
    { MP_ROM_QSTR(MP_QSTR_INT8), MP_ROM_INT(TYPE2SMALLINT(INT8, 4)) },
677
    /// \constant UINT16 - uint16_t value type
678
    { MP_ROM_QSTR(MP_QSTR_UINT16), MP_ROM_INT(TYPE2SMALLINT(UINT16, 4)) },
679
    /// \constant INT16 - int16_t value type
680
    { MP_ROM_QSTR(MP_QSTR_INT16), MP_ROM_INT(TYPE2SMALLINT(INT16, 4)) },
681
    /// \constant UINT32 - uint32_t value type
682
    { MP_ROM_QSTR(MP_QSTR_UINT32), MP_ROM_INT(TYPE2SMALLINT(UINT32, 4)) },
683
    /// \constant INT32 - int32_t value type
684
    { MP_ROM_QSTR(MP_QSTR_INT32), MP_ROM_INT(TYPE2SMALLINT(INT32, 4)) },
685
    /// \constant UINT64 - uint64_t value type
686
    { MP_ROM_QSTR(MP_QSTR_UINT64), MP_ROM_INT(TYPE2SMALLINT(UINT64, 4)) },
687
    /// \constant INT64 - int64_t value type
688
    { MP_ROM_QSTR(MP_QSTR_INT64), MP_ROM_INT(TYPE2SMALLINT(INT64, 4)) },
689

690
691
692
693
694
695
    { MP_ROM_QSTR(MP_QSTR_BFUINT8), MP_ROM_INT(TYPE2SMALLINT(BFUINT8, 4)) },
    { MP_ROM_QSTR(MP_QSTR_BFINT8), MP_ROM_INT(TYPE2SMALLINT(BFINT8, 4)) },
    { MP_ROM_QSTR(MP_QSTR_BFUINT16), MP_ROM_INT(TYPE2SMALLINT(BFUINT16, 4)) },
    { MP_ROM_QSTR(MP_QSTR_BFINT16), MP_ROM_INT(TYPE2SMALLINT(BFINT16, 4)) },
    { MP_ROM_QSTR(MP_QSTR_BFUINT32), MP_ROM_INT(TYPE2SMALLINT(BFUINT32, 4)) },
    { MP_ROM_QSTR(MP_QSTR_BFINT32), MP_ROM_INT(TYPE2SMALLINT(BFINT32, 4)) },
696

697
698
    { MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(17) },
    { MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(22) },
699

700
701
702
703
704
    #if MICROPY_PY_BUILTINS_FLOAT
    { MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, 4)) },
    { MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) },
    #endif

705
706
    { MP_ROM_QSTR(MP_QSTR_PTR), MP_ROM_INT(TYPE2SMALLINT(PTR, AGG_TYPE_BITS)) },
    { MP_ROM_QSTR(MP_QSTR_ARRAY), MP_ROM_INT(TYPE2SMALLINT(ARRAY, AGG_TYPE_BITS)) },
707
708
};

709
STATIC MP_DEFINE_CONST_DICT(mp_module_uctypes_globals, mp_module_uctypes_globals_table);
710
711
712
713
714
715
716

const mp_obj_module_t mp_module_uctypes = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t*)&mp_module_uctypes_globals,
};

#endif