moductypes: Add types.
This is an experiment. I want the usual Python guarantee that you can't just footgun yourself by passing the wrong thing through a pointer argument, at least as much as is practical. But a uctypes struct doesn't have a type of its own, like Point or Rect; you can only check that the *size* matches some underlying expected size. This change allows a type like Rect to be defined in the core, and of course if it's a distinct Python type it can be checked in the core as well. Restore the unix tests so we can test the added core functionality. Signed-off-by: Jeff Epler <jepler@gmail.com>
This commit is contained in:
parent
a80f00bac2
commit
13fbd43610
6 changed files with 606 additions and 87 deletions
298
.github/workflows/ports_unix.yml
vendored
Normal file
298
.github/workflows/ports_unix.yml
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
name: unix port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'examples/**'
|
||||
- 'mpy-cross/**'
|
||||
- 'ports/unix/**'
|
||||
- 'tests/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
minimal:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_minimal_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_minimal_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
reproducible:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build with reproducible date
|
||||
run: source tools/ci.sh && ci_unix_minimal_build
|
||||
env:
|
||||
SOURCE_DATE_EPOCH: 1234567890
|
||||
- name: Check reproducible build date
|
||||
run: echo | ports/unix/build-minimal/micropython -i | grep 'on 2009-02-13;'
|
||||
|
||||
standard:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_standard_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_standard_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
standard_v2:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_standard_v2_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_standard_v2_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
|
||||
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_coverage_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_coverage_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_tests
|
||||
- name: Test merging .mpy files
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_mpy_merge_tests
|
||||
- name: Build native mpy modules
|
||||
run: source tools/ci.sh && ci_native_mpy_modules_build
|
||||
- name: Test importing .mpy generated by mpy_ld.py
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests
|
||||
- name: Run gcov coverage analysis
|
||||
run: |
|
||||
(cd ports/unix && gcov -o build-coverage/py ../../py/*.c || true)
|
||||
(cd ports/unix && gcov -o build-coverage/extmod ../../extmod/*.c || true)
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
coverage_32bit:
|
||||
runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_32bit_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_coverage_32bit_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_coverage_32bit_run_tests
|
||||
- name: Build native mpy modules
|
||||
run: source tools/ci.sh && ci_native_mpy_modules_32bit_build
|
||||
- name: Test importing .mpy generated by mpy_ld.py
|
||||
run: source tools/ci.sh && ci_unix_coverage_32bit_run_native_mpy_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
nanbox:
|
||||
runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_32bit_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_nanbox_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_nanbox_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
float:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_float_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_float_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_float_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
stackless_clang:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_clang_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_stackless_clang_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_stackless_clang_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
float_clang:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_clang_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_float_clang_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_float_clang_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
settrace_stackless:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
|
||||
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_settrace_stackless_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_settrace_stackless_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_macos_build
|
||||
- name: Run tests
|
||||
run: source tools/ci.sh && ci_unix_macos_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
qemu_mips:
|
||||
# ubuntu-22.04 is needed for older libffi.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_qemu_mips_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_qemu_mips_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_qemu_mips_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
qemu_arm:
|
||||
# ubuntu-22.04 is needed for older libffi.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_qemu_arm_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_qemu_arm_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_qemu_arm_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
qemu_riscv64:
|
||||
# ubuntu-22.04 is needed for older libffi.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_qemu_riscv64_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_qemu_riscv64_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_qemu_riscv64_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
sanitize_address:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
|
||||
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_coverage_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_sanitize_address_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_sanitize_address_run_tests
|
||||
- name: Test merging .mpy files
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_mpy_merge_tests
|
||||
- name: Build native mpy modules
|
||||
run: source tools/ci.sh && ci_native_mpy_modules_build
|
||||
- name: Test importing .mpy generated by mpy_ld.py
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
sanitize_undefined:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
|
||||
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_coverage_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_sanitize_undefined_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_sanitize_undefined_run_tests
|
||||
- name: Test merging .mpy files
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_mpy_merge_tests
|
||||
- name: Build native mpy modules
|
||||
run: source tools/ci.sh && ci_native_mpy_modules_build
|
||||
- name: Test importing .mpy generated by mpy_ld.py
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
|
@ -31,49 +31,12 @@
|
|||
#include "py/runtime.h"
|
||||
#include "py/objtuple.h"
|
||||
#include "py/binary.h"
|
||||
#include "extmod/moductypes.h"
|
||||
|
||||
#if MICROPY_PY_UCTYPES
|
||||
|
||||
// The uctypes module allows defining the layout of a raw data structure (using
|
||||
// terms of the 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.
|
||||
|
||||
#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
|
||||
#define LEN_BITS (OFFSET_BITS + BITF_OFF_BITS)
|
||||
#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,
|
||||
};
|
||||
|
||||
// Here we need to set sign bit right
|
||||
#define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 1)
|
||||
#define GET_TYPE(x, nbits) (((x) >> (31 - nbits)) & ((1 << nbits) - 1))
|
||||
// Bit 0 is "is_signed"
|
||||
#define GET_SCALAR_SIZE(val_type) (1 << ((val_type) >> 1))
|
||||
#define VALUE_MASK(type_nbits) ~((int)0x80000000 >> type_nbits)
|
||||
|
||||
#define IS_SCALAR_ARRAY(tuple_desc) ((tuple_desc)->len == 2)
|
||||
// We cannot apply the below to INT8, as their range [-128, 127]
|
||||
|
|
@ -82,6 +45,9 @@ enum {
|
|||
// "struct" in uctypes context means "structural", i.e. aggregate, type.
|
||||
static const mp_obj_type_t uctypes_struct_type;
|
||||
|
||||
// Get size of any type descriptor
|
||||
static mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size);
|
||||
|
||||
typedef struct _mp_obj_uctypes_struct_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t desc;
|
||||
|
|
@ -93,19 +59,106 @@ static MP_NORETURN void syntax_error(void) {
|
|||
mp_raise_TypeError(MP_ERROR_TEXT("syntax error in uctypes descriptor"));
|
||||
}
|
||||
|
||||
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) {
|
||||
mp_arg_check_num(n_args, n_kw, 2, 3, false);
|
||||
mp_obj_uctypes_struct_t *o = mp_obj_malloc(mp_obj_uctypes_struct_t, type);
|
||||
o->addr = (void *)(uintptr_t)mp_obj_get_int_truncated(args[0]);
|
||||
o->desc = args[1];
|
||||
o->flags = LAYOUT_NATIVE;
|
||||
if (n_args == 3) {
|
||||
o->flags = mp_obj_get_int(args[2]);
|
||||
static bool is_struct_type(mp_obj_t obj_in) {
|
||||
if (!mp_obj_is_type(obj_in, &mp_type_type)) {
|
||||
return false;
|
||||
}
|
||||
mp_make_new_fun_t make_new = MP_OBJ_TYPE_GET_SLOT_OR_NULL((mp_obj_type_t *)MP_OBJ_TO_PTR(obj_in), make_new);
|
||||
return make_new == uctypes_struct_type_make_new;
|
||||
}
|
||||
|
||||
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) {
|
||||
mp_arg_check_num(n_args, n_kw, 2, 3, false);
|
||||
mp_obj_t desc = args[1];
|
||||
int flags = LAYOUT_NATIVE;
|
||||
if (is_struct_type(desc)) {
|
||||
type = MP_OBJ_TO_PTR(desc);
|
||||
mp_obj_ctypes_struct_type_t *struct_type = (mp_obj_ctypes_struct_type_t *)type;
|
||||
desc = struct_type->desc;
|
||||
} else if (n_args == 3) {
|
||||
flags = mp_obj_get_int(args[2]);
|
||||
}
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_obj_uctypes_struct_t *o = mp_obj_malloc(mp_obj_uctypes_struct_t, type);
|
||||
if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) {
|
||||
o->addr = bufinfo.buf;
|
||||
} else {
|
||||
o->addr = (void *)(uintptr_t)mp_obj_get_int_truncated(args[0]);
|
||||
}
|
||||
o->desc = desc;
|
||||
o->flags = flags;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
static void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
static bool is_ptr(mp_obj_t desc) {
|
||||
if (!mp_obj_is_type(desc, &mp_type_tuple)) {
|
||||
return false;
|
||||
}
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(desc);
|
||||
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
|
||||
uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
|
||||
return agg_type == PTR;
|
||||
}
|
||||
|
||||
mp_obj_t uctypes_struct_type_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_obj_ctypes_struct_type_t *type = (mp_obj_ctypes_struct_type_t *)type_in;
|
||||
mp_obj_t desc = type->desc;
|
||||
mp_uint_t max_field_size = 0;
|
||||
mp_uint_t size = uctypes_struct_size(desc, type->struct_flags, &max_field_size);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_obj_t bytearray = mp_obj_new_bytearray(size, NULL);
|
||||
mp_get_buffer_raise(bytearray, &bufinfo, MP_BUFFER_WRITE);
|
||||
if (is_ptr(desc)) {
|
||||
if (n_args != 1) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
if (mp_obj_is_int(args[0])) {
|
||||
void *ptr = (void *)(uintptr_t)mp_obj_get_int_truncated(args[0]);
|
||||
*(void **)bufinfo.buf = ptr;
|
||||
} else {
|
||||
mp_buffer_info_t pointee;
|
||||
mp_get_buffer_raise(args[0], &pointee, MP_BUFFER_WRITE);
|
||||
*(void **)bufinfo.buf = pointee.buf;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t args1[] = {mp_obj_new_int((intptr_t)bufinfo.buf), desc, mp_obj_new_int(type->struct_flags)};
|
||||
mp_obj_t result = uctypes_struct_make_new(type_in, 3, 0, args1);
|
||||
|
||||
if (mp_obj_is_type(desc, &mp_type_tuple)) {
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(desc);
|
||||
mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]);
|
||||
uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS);
|
||||
if (agg_type == ARRAY) {
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
mp_obj_subscr(result, mp_obj_new_int(i), args[i]);
|
||||
}
|
||||
} else if (agg_type != PTR) {
|
||||
syntax_error();
|
||||
}
|
||||
} else {
|
||||
mp_obj_dict_t *d = MP_OBJ_TO_PTR(desc);
|
||||
// only for packed ROM tables..
|
||||
assert(d->map.alloc == d->map.used);
|
||||
assert(d->map.all_keys_are_qstrs);
|
||||
|
||||
if (n_args > d->map.alloc) {
|
||||
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("struct: index out of range"));
|
||||
}
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
mp_store_attr(result, mp_obj_str_get_qstr(d->map.table[i].key), args[i]);
|
||||
}
|
||||
}
|
||||
args += n_args;
|
||||
for (size_t i = 0; i < n_kw; i += 2) {
|
||||
mp_store_attr(result, mp_obj_str_get_qstr(args[i]), args[i + 1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define mp_obj_get_type_qstr(o_in) ((qstr)(mp_obj_get_type((o_in))->name))
|
||||
void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *typen = "unk";
|
||||
|
|
@ -126,11 +179,9 @@ static void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_p
|
|||
} else {
|
||||
typen = "ERROR";
|
||||
}
|
||||
mp_printf(print, "<struct %s %p>", typen, self->addr);
|
||||
}
|
||||
|
||||
// Get size of any type descriptor
|
||||
static mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size);
|
||||
mp_printf(print, "<%q %s %p>", (qstr)mp_obj_get_type_qstr(self_in), typen, self->addr);
|
||||
}
|
||||
|
||||
// Get size of scalar type descriptor
|
||||
static inline mp_uint_t uctypes_struct_scalar_size(int val_type) {
|
||||
|
|
@ -195,6 +246,10 @@ static mp_uint_t uctypes_struct_agg_size(mp_obj_tuple_t *t, int layout_type, mp_
|
|||
}
|
||||
|
||||
static mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size) {
|
||||
if (is_struct_type(desc_in)) {
|
||||
mp_obj_ctypes_struct_type_t *struct_type = MP_OBJ_TO_PTR(desc_in);
|
||||
desc_in = struct_type->desc;
|
||||
}
|
||||
if (!mp_obj_is_dict_or_ordereddict(desc_in)) {
|
||||
if (mp_obj_is_type(desc_in, &mp_type_tuple)) {
|
||||
return uctypes_struct_agg_size((mp_obj_tuple_t *)MP_OBJ_TO_PTR(desc_in), layout_type, max_field_size);
|
||||
|
|
@ -253,20 +308,22 @@ static mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_
|
|||
static mp_obj_t uctypes_struct_sizeof(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t obj_in = args[0];
|
||||
mp_uint_t max_field_size = 0;
|
||||
if (mp_obj_is_type(obj_in, &mp_type_bytearray)) {
|
||||
return mp_obj_len(obj_in);
|
||||
}
|
||||
int layout_type = LAYOUT_NATIVE;
|
||||
// We can apply sizeof either to structure definition (a dict)
|
||||
// or to instantiated structure
|
||||
if (mp_obj_is_type(obj_in, &uctypes_struct_type)) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
if (mp_get_buffer(obj_in, &bufinfo, MP_BUFFER_READ)) {
|
||||
if (n_args != 1) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
// Extract structure definition
|
||||
mp_obj_uctypes_struct_t *obj = MP_OBJ_TO_PTR(obj_in);
|
||||
return MP_OBJ_NEW_SMALL_INT(bufinfo.len);
|
||||
}
|
||||
int layout_type = LAYOUT_NATIVE;
|
||||
// We can apply sizeof to structure definition (a dict) or a "struct type" type
|
||||
if (is_struct_type(obj_in)) {
|
||||
if (n_args != 1) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
mp_obj_ctypes_struct_type_t *obj = MP_OBJ_TO_PTR(obj_in);
|
||||
obj_in = obj->desc;
|
||||
layout_type = obj->flags;
|
||||
layout_type = obj->struct_flags;
|
||||
} else {
|
||||
if (n_args == 2) {
|
||||
layout_type = mp_obj_get_int(args[1]);
|
||||
|
|
@ -277,6 +334,15 @@ static mp_obj_t uctypes_struct_sizeof(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, uctypes_struct_sizeof);
|
||||
|
||||
static mp_obj_t uctypes_struct_desc(mp_obj_t arg) {
|
||||
if (!is_struct_type(arg)) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
mp_obj_ctypes_struct_type_t *struct_type = MP_OBJ_TO_PTR(arg);
|
||||
return struct_type->desc;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_desc_obj, uctypes_struct_desc);
|
||||
|
||||
static const char type2char[16] = {
|
||||
'B', 'b', 'H', 'h', 'I', 'i', 'Q', 'q',
|
||||
'-', '-', '-', '-', '-', '-', 'f', 'd'
|
||||
|
|
@ -478,11 +544,8 @@ static mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
|||
|
||||
switch (agg_type) {
|
||||
case STRUCT: {
|
||||
mp_obj_uctypes_struct_t *o = mp_obj_malloc(mp_obj_uctypes_struct_t, &uctypes_struct_type);
|
||||
o->desc = sub->items[1];
|
||||
o->addr = self->addr + offset;
|
||||
o->flags = self->flags;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
mp_obj_t args[] = { mp_obj_new_int((mp_uint_t)(uintptr_t)self->addr + offset), sub->items[1], mp_obj_new_int(self->flags) };
|
||||
return uctypes_struct_make_new(&uctypes_struct_type, MP_ARRAY_SIZE(args), 0, args);
|
||||
}
|
||||
case ARRAY: {
|
||||
mp_uint_t dummy;
|
||||
|
|
@ -493,11 +556,8 @@ static mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
|||
MP_FALLTHROUGH
|
||||
}
|
||||
case PTR: {
|
||||
mp_obj_uctypes_struct_t *o = mp_obj_malloc(mp_obj_uctypes_struct_t, &uctypes_struct_type);
|
||||
o->desc = MP_OBJ_FROM_PTR(sub);
|
||||
o->addr = self->addr + offset;
|
||||
o->flags = self->flags;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
mp_obj_t args[] = { mp_obj_new_int((mp_uint_t)(uintptr_t)self->addr + offset), deref, mp_obj_new_int(self->flags) };
|
||||
return uctypes_struct_make_new(&uctypes_struct_type, MP_ARRAY_SIZE(args), 0, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +565,7 @@ static mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
|||
return MP_OBJ_NULL;
|
||||
}
|
||||
|
||||
static void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
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);
|
||||
|
|
@ -518,7 +578,7 @@ static void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
|||
}
|
||||
}
|
||||
|
||||
static mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
|
||||
mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
|
||||
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
if (value == MP_OBJ_NULL) {
|
||||
|
|
@ -565,11 +625,8 @@ static mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob
|
|||
} else if (value == MP_OBJ_SENTINEL) {
|
||||
mp_uint_t dummy = 0;
|
||||
mp_uint_t size = uctypes_struct_size(t->items[2], self->flags, &dummy);
|
||||
mp_obj_uctypes_struct_t *o = mp_obj_malloc(mp_obj_uctypes_struct_t, &uctypes_struct_type);
|
||||
o->desc = t->items[2];
|
||||
o->addr = self->addr + size * index;
|
||||
o->flags = self->flags;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
mp_obj_t args[] = { mp_obj_new_int((mp_uint_t)(uintptr_t)self->addr + size * index), t->items[2], mp_obj_new_int(self->flags) };
|
||||
return uctypes_struct_make_new(&uctypes_struct_type, MP_ARRAY_SIZE(args), 0, args);
|
||||
} else {
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
}
|
||||
|
|
@ -578,15 +635,17 @@ static mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob
|
|||
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);
|
||||
if (value == MP_OBJ_SENTINEL) {
|
||||
return get_aligned(val_type, p, index);
|
||||
} else {
|
||||
set_aligned(val_type, p, index, value);
|
||||
return value; // just !MP_OBJ_NULL
|
||||
}
|
||||
} else {
|
||||
mp_uint_t dummy = 0;
|
||||
mp_uint_t size = uctypes_struct_size(t->items[1], self->flags, &dummy);
|
||||
mp_obj_uctypes_struct_t *o = mp_obj_malloc(mp_obj_uctypes_struct_t, &uctypes_struct_type);
|
||||
o->desc = t->items[1];
|
||||
o->addr = p + size * index;
|
||||
o->flags = self->flags;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
mp_obj_t args[] = { mp_obj_new_int((mp_uint_t)(uintptr_t)p + size * index), t->items[1], mp_obj_new_int(self->flags) };
|
||||
return uctypes_struct_make_new(&uctypes_struct_type, MP_ARRAY_SIZE(args), 0, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -595,7 +654,7 @@ static mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob
|
|||
}
|
||||
}
|
||||
|
||||
static mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
|
||||
mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
|
||||
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
switch (op) {
|
||||
case MP_UNARY_OP_INT_MAYBE:
|
||||
|
|
@ -615,7 +674,7 @@ static mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
|
|||
}
|
||||
}
|
||||
|
||||
static mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
|
||||
mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
|
||||
(void)flags;
|
||||
mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_uint_t max_field_size = 0;
|
||||
|
|
@ -669,6 +728,7 @@ static const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = {
|
|||
{ 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) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_descr), MP_ROM_PTR(&uctypes_struct_desc_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_NATIVE), MP_ROM_INT(LAYOUT_NATIVE) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LITTLE_ENDIAN), MP_ROM_INT(LAYOUT_LITTLE_ENDIAN) },
|
||||
|
|
|
|||
136
extmod/moductypes.h
Normal file
136
extmod/moductypes.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2018 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.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_PY_MODUCTYPES_H
|
||||
#define MICROPY_INCLUDED_PY_MODUCTYPES_H
|
||||
|
||||
#if MICROPY_PY_UCTYPES
|
||||
#include "py/obj.h"
|
||||
|
||||
typedef struct _mp_obj_ctypes_struct_type_t {
|
||||
// This is a mp_obj_type_t with six slots.
|
||||
mp_obj_empty_type_t base;
|
||||
void *slots[6];
|
||||
mp_obj_t desc;
|
||||
uint32_t struct_flags;
|
||||
} mp_obj_ctypes_struct_type_t;
|
||||
|
||||
void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
|
||||
void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
|
||||
mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value);
|
||||
mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in);
|
||||
mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);
|
||||
mp_obj_t uctypes_struct_type_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
|
||||
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);
|
||||
|
||||
#define MP_DECLARE_CTYPES_STRUCT(type_name) \
|
||||
extern mp_obj_ctypes_struct_type_t type_name;
|
||||
|
||||
#define MP_DEFINE_CTYPES_STRUCT(type_name, name_, desc_, flags_) \
|
||||
mp_obj_ctypes_struct_type_t type_name = { \
|
||||
.base = { \
|
||||
.base = { &mp_type_type }, \
|
||||
.flags = MP_TYPE_FLAG_NONE, \
|
||||
.name = name_, \
|
||||
.slot_index_make_new = 1, \
|
||||
.slot_index_print = 2, \
|
||||
.slot_index_attr = 3, \
|
||||
.slot_index_subscr = 4, \
|
||||
.slot_index_unary_op = 5, \
|
||||
.slot_index_buffer = 6, \
|
||||
}, \
|
||||
.slots = { \
|
||||
uctypes_struct_type_make_new, \
|
||||
uctypes_struct_print, \
|
||||
uctypes_struct_attr, \
|
||||
uctypes_struct_subscr, \
|
||||
uctypes_struct_unary_op, \
|
||||
uctypes_get_buffer \
|
||||
}, \
|
||||
.desc = desc_, \
|
||||
.struct_flags = flags_, \
|
||||
}
|
||||
|
||||
// The uctypes module allows defining the layout of a raw data structure (using
|
||||
// terms of the 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.
|
||||
|
||||
#define LAYOUT_LITTLE_ENDIAN (0)
|
||||
#define LAYOUT_BIG_ENDIAN (1)
|
||||
#define LAYOUT_NATIVE (2)
|
||||
|
||||
#define LEN_BITS (OFFSET_BITS + BITF_OFF_BITS)
|
||||
#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B
|
||||
#define VAL_TYPE_BITS 4
|
||||
#define BITF_LEN_BITS 5
|
||||
#define BITF_OFF_BITS 5
|
||||
#define OFFSET_BITS 16
|
||||
#if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 30
|
||||
#error Invalid encoding field length
|
||||
#endif
|
||||
// Here we need to set sign bit right
|
||||
#define TYPE2SMALLINT(x, nbits) ((((int)x) << (32 - nbits)) >> 2)
|
||||
#define GET_TYPE(x, nbits) (((x) >> (30 - nbits)) & ((1 << nbits) - 1))
|
||||
#define VALUE_MASK(type_nbits) (~((int)0xc0000000 >> type_nbits))
|
||||
#else
|
||||
#define VAL_TYPE_BITS 4
|
||||
#define BITF_LEN_BITS 5
|
||||
#define BITF_OFF_BITS 5
|
||||
#define OFFSET_BITS 17
|
||||
#define LEN_BITS (OFFSET_BITS + BITF_OFF_BITS)
|
||||
#if VAL_TYPE_BITS + BITF_LEN_BITS + BITF_OFF_BITS + OFFSET_BITS != 31
|
||||
#error Invalid encoding field length
|
||||
#endif
|
||||
// Here we need to set sign bit right
|
||||
#define TYPE2SMALLINT(x, nbits) ((((mp_int_t)x) << (32 - nbits)) >> 1)
|
||||
#define GET_TYPE(x, nbits) (((x) >> (31 - nbits)) & ((1 << nbits) - 1))
|
||||
#define VALUE_MASK(type_nbits) (~((int)0x80000000 >> type_nbits))
|
||||
#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,
|
||||
};
|
||||
|
||||
// Here we need to set sign bit right
|
||||
#define UCTYPE_TYPE(t) (TYPE2SMALLINT((t), VAL_TYPE_BITS))
|
||||
#define UCTYPE_AGG(a) (TYPE2SMALLINT((a), AGG_TYPE_BITS))
|
||||
#define TYPE_AND_OFFSET(t, o) (UCTYPE_TYPE((t)) | (o))
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -10,7 +10,7 @@ data = bytearray(b"01234567")
|
|||
|
||||
# first argument isn't an integer
|
||||
try:
|
||||
uctypes.struct(data, {})
|
||||
uctypes.struct([], {})
|
||||
except TypeError:
|
||||
print("TypeError")
|
||||
|
||||
|
|
|
|||
19
tests/extmod/uctypes_rect.py
Normal file
19
tests/extmod/uctypes_rect.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
try:
|
||||
Rect
|
||||
except NameError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
import uctypes
|
||||
|
||||
r = Rect()
|
||||
print(f"{repr(r)[:13]!r}")
|
||||
print(r.top, r.left, r.bottom, r.right)
|
||||
r = Rect(1, 2, 3, 4)
|
||||
print(r.top, r.left, r.bottom, r.right)
|
||||
r = Rect(1, 2, right=4)
|
||||
print(r.top, r.left, r.bottom, r.right)
|
||||
|
||||
r = uctypes.struct(uctypes.addressof(r), Rect)
|
||||
print(f"{repr(r)[:13]!r}")
|
||||
print(r.top, r.left, r.bottom, r.right)
|
||||
6
tests/extmod/uctypes_rect.py.exp
Normal file
6
tests/extmod/uctypes_rect.py.exp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
'<Rect STRUCT '
|
||||
0 0 0 0
|
||||
1 2 3 4
|
||||
1 2 0 4
|
||||
'<Rect STRUCT '
|
||||
1 2 0 4
|
||||
Loading…
Reference in a new issue