diff --git a/py/mpconfig.h b/py/mpconfig.h index ed42fbccc1..acc5f66493 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -409,6 +409,12 @@ typedef double mp_float_t; #define MICROPY_PY_ARRAY (1) #endif +// Whether to support slice assignments for array (and bytearray). +// This is rarely used, but adds ~0.5K of code. +#ifndef MICROPY_PY_ARRAY_SLICE_ASSIGN +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (0) +#endif + // Whether to provide "collections" module #ifndef MICROPY_PY_COLLECTIONS #define MICROPY_PY_COLLECTIONS (1) diff --git a/py/obj.h b/py/obj.h index db42e795a2..bfe28e1e41 100644 --- a/py/obj.h +++ b/py/obj.h @@ -616,15 +616,15 @@ mp_obj_t mp_seq_count_obj(const mp_obj_t *items, mp_uint_t len, mp_obj_t value); mp_obj_t mp_seq_extract_slice(mp_uint_t len, const mp_obj_t *seq, mp_bound_slice_t *indexes); // Helper to clear stale pointers from allocated, but unused memory, to preclude GC problems #define mp_seq_clear(start, len, alloc_len, item_sz) memset((byte*)(start) + (len) * (item_sz), 0, ((alloc_len) - (len)) * (item_sz)) -#define mp_seq_replace_slice_no_grow(dest, dest_len, beg, end, slice, slice_len, item_t) \ - /*printf("memcpy(%p, %p, %d)\n", dest + beg, slice, slice_len * sizeof(item_t));*/ \ - memcpy(dest + beg, slice, slice_len * sizeof(item_t)); \ - /*printf("memmove(%p, %p, %d)\n", dest + (beg + slice_len), dest + end, (dest_len - end) * sizeof(item_t));*/ \ - memmove(dest + (beg + slice_len), dest + end, (dest_len - end) * sizeof(item_t)); +#define mp_seq_replace_slice_no_grow(dest, dest_len, beg, end, slice, slice_len, item_sz) \ + /*printf("memcpy(%p, %p, %d)\n", dest + beg, slice, slice_len * (item_sz));*/ \ + memcpy(((char*)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); \ + /*printf("memmove(%p, %p, %d)\n", dest + (beg + slice_len), dest + end, (dest_len - end) * (item_sz));*/ \ + memmove(((char*)dest) + (beg + slice_len) * (item_sz), ((char*)dest) + (end) * (item_sz), (dest_len - end) * (item_sz)); -#define mp_seq_replace_slice_grow_inplace(dest, dest_len, beg, end, slice, slice_len, len_adj, item_t) \ - /*printf("memmove(%p, %p, %d)\n", dest + beg + len_adj, dest + beg, (dest_len - beg) * sizeof(item_t));*/ \ - memmove(dest + beg + len_adj, dest + beg, (dest_len - beg) * sizeof(item_t)); \ - memcpy(dest + beg, slice, slice_len * sizeof(item_t)); +#define mp_seq_replace_slice_grow_inplace(dest, dest_len, beg, end, slice, slice_len, len_adj, item_sz) \ + /*printf("memmove(%p, %p, %d)\n", dest + beg + len_adj, dest + beg, (dest_len - beg) * (item_sz));*/ \ + memmove(((char*)dest) + (beg + len_adj) * (item_sz), ((char*)dest) + (beg) * (item_sz), (dest_len - beg) * (item_sz)); \ + memcpy(((char*)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); #endif // __MICROPY_INCLUDED_PY_OBJ_H__ diff --git a/py/objarray.c b/py/objarray.c index e2fee1f2ed..e030548bbb 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -351,17 +351,48 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value if (0) { #if MICROPY_PY_BUILTINS_SLICE } else if (MP_OBJ_IS_TYPE(index_in, &mp_type_slice)) { - if (value != MP_OBJ_SENTINEL) { - // Only getting a slice is suported so far, not assignment - // TODO: confirmed that both bytearray and array.array support - // slice assignment (incl. of different size) - return MP_OBJ_NULL; // op not supported - } mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(o->len, index_in, &slice)) { nlr_raise(mp_obj_new_exception_msg(&mp_type_NotImplementedError, "only slices with step=1 (aka None) are supported")); } + if (value != MP_OBJ_SENTINEL) { + #if MICROPY_PY_ARRAY_SLICE_ASSIGN + // Assign + if (!MP_OBJ_IS_TYPE(value, &mp_type_array) && !MP_OBJ_IS_TYPE(value, &mp_type_bytearray)) { + mp_not_implemented("array required on right side"); + } + mp_obj_array_t *src_slice = value; + int item_sz = mp_binary_get_size('@', o->typecode, NULL); + if (item_sz != mp_binary_get_size('@', src_slice->typecode, NULL)) { + mp_not_implemented("arrays should be compatible"); + } + + // TODO: check src/dst compat + mp_int_t len_adj = src_slice->len - (slice.stop - slice.start); + if (len_adj > 0) { + if (len_adj > o->free) { + // TODO: alloc policy; at the moment we go conservative + o->items = m_realloc(o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz); + o->free = 0; + } + mp_seq_replace_slice_grow_inplace(o->items, o->len, + slice.start, slice.stop, src_slice->items, src_slice->len, len_adj, item_sz); + } else { + mp_seq_replace_slice_no_grow(o->items, o->len, + slice.start, slice.stop, src_slice->items, src_slice->len, item_sz); + // Clear "freed" elements at the end of list + // TODO: This is actually only needed for typecode=='O' + mp_seq_clear(o->items, o->len + len_adj, o->len, item_sz); + // TODO: alloc policy after shrinking + } + o->len += len_adj; + return mp_const_none; + #else + return MP_OBJ_NULL; // op not supported + #endif + } + mp_obj_array_t *res; int sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL); assert(sz > 0); diff --git a/py/objlist.c b/py/objlist.c index e0c8953755..03e3dda033 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -165,7 +165,7 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_int_t len_adj = slice.start - slice.stop; //printf("Len adj: %d\n", len_adj); assert(len_adj <= 0); - mp_seq_replace_slice_no_grow(self->items, self->len, slice.start, slice.stop, self->items/*NULL*/, 0, mp_obj_t); + mp_seq_replace_slice_no_grow(self->items, self->len, slice.start, slice.stop, self->items/*NULL*/, 0, sizeof(*self->items)); // Clear "freed" elements at the end of list mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); self->len += len_adj; @@ -211,10 +211,10 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { self->alloc = self->len + len_adj; } mp_seq_replace_slice_grow_inplace(self->items, self->len, - slice_out.start, slice_out.stop, slice->items, slice->len, len_adj, mp_obj_t); + slice_out.start, slice_out.stop, slice->items, slice->len, len_adj, sizeof(*self->items)); } else { mp_seq_replace_slice_no_grow(self->items, self->len, - slice_out.start, slice_out.stop, slice->items, slice->len, mp_obj_t); + slice_out.start, slice_out.stop, slice->items, slice->len, sizeof(*self->items)); // Clear "freed" elements at the end of list mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); // TODO: apply allocation policy re: alloc_size diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index bbfae7b21b..8c28915631 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -60,6 +60,7 @@ #define MICROPY_PY_BUILTINS_COMPILE (1) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_PY_SYS_EXIT (1) #define MICROPY_PY_SYS_PLATFORM "linux" #define MICROPY_PY_SYS_MAXSIZE (1)