Skip to content

py/modmicropython: Add micropython.memmove() and micropython.memset(). #12487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions py/modmicropython.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
*/

#include <stdio.h>
#include <string.h>

#include "py/builtin.h"
#include "py/obj.h"
#include "py/stackctrl.h"
#include "py/runtime.h"
#include "py/gc.h"
Expand Down Expand Up @@ -166,6 +168,96 @@ STATIC mp_obj_t mp_micropython_schedule(mp_obj_t function, mp_obj_t arg) {
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_schedule);
#endif

#if MICROPY_ENABLE_MEM_FUNCTIONS

STATIC mp_obj_t mp_micropython_memmove(size_t n_args, const mp_obj_t *args) {
enum { ARG_dest, ARG_dest_idx, ARG_src, ARG_src_idx, ARG_len }; // len is optional
mp_buffer_info_t src, dest;
mp_int_t src_idx, dest_idx, len, max_len;

mp_get_buffer_raise(args[ARG_dest], &dest, MP_BUFFER_WRITE);
mp_get_buffer_raise(args[ARG_src], &src, MP_BUFFER_READ);
dest_idx = mp_obj_get_int(args[ARG_dest_idx]);
src_idx = mp_obj_get_int(args[ARG_src_idx]);

// similar to slice syntax, negative indexes are relative to the end
if (dest_idx < 0) {
dest_idx += dest.len;
}
if (src_idx < 0) {
src_idx += src.len;
}

if (dest_idx < 0 || src_idx < 0 || (size_t)dest_idx >= dest.len || (size_t)src_idx >= src.len) {
// TODO: Maybe it's better to emulate slice syntax here and have ranges
// past the end silently truncate to the end?
mp_raise_type(&mp_type_IndexError); // TODO: ValueError? IndexError is what equivalent Python code would raise
}

// The most bytes we can move
max_len = MIN(dest.len - dest_idx, src.len - src_idx);

if (n_args > ARG_len) {
len = mp_obj_get_int(args[ARG_len]);
if (len < 0 || len > max_len) {
// TODO: maybe better to truncate here as well?
mp_raise_type(&mp_type_IndexError); // TODO: ValueError? see above
}
} else {
len = max_len;
}

memmove((char *)dest.buf + dest_idx, (char *)src.buf + src_idx, len);

return mp_obj_new_int(len);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_memmove_obj, 4, 5, mp_micropython_memmove);

STATIC mp_obj_t mp_micropython_memset(size_t n_args, const mp_obj_t *args) {
enum { ARG_dest, ARG_dest_idx, ARG_c, ARG_len };
mp_buffer_info_t dest;
mp_int_t dest_idx = 0;
uint8_t c = 0;
mp_int_t len;

mp_get_buffer_raise(args[ARG_dest], &dest, MP_BUFFER_WRITE);
if (n_args > ARG_dest_idx) {
dest_idx = mp_obj_get_int(args[ARG_dest_idx]);

if (dest_idx < 0) {
// Like slice syntax, negative value indexes from the end
dest_idx += dest.len;
}

if (dest_idx < 0 || (size_t)dest_idx >= dest.len) {
mp_raise_type(&mp_type_IndexError); // TODO: ValueError? see above
}
}

if (n_args > ARG_c) {
// To save code size, don't range-check if c is a single byte integer,
// out of range values are truncated and then used.
c = mp_obj_get_int(args[ARG_c]);
}

if (n_args > ARG_len) {
len = mp_obj_get_int(args[ARG_len]);
if (len < 0 || (size_t)len > dest.len - dest_idx) {
mp_raise_type(&mp_type_IndexError); // TODO: ValueError? see above
}
} else {
len = dest.len - dest_idx;
}

memset((char *)dest.buf + dest_idx, c, len);

return mp_obj_new_int(len);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_memset_obj, 1, 4, mp_micropython_memset);

#endif


STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) },
{ MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) },
Expand Down Expand Up @@ -203,6 +295,10 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
#if MICROPY_ENABLE_SCHEDULER
{ MP_ROM_QSTR(MP_QSTR_schedule), MP_ROM_PTR(&mp_micropython_schedule_obj) },
#endif
#if MICROPY_ENABLE_MEM_FUNCTIONS
{ MP_ROM_QSTR(MP_QSTR_memmove), MP_ROM_PTR(&mp_micropython_memmove_obj) },
{ MP_ROM_QSTR(MP_QSTR_memset), MP_ROM_PTR(&mp_micropython_memset_obj) },
#endif
};

STATIC MP_DEFINE_CONST_DICT(mp_module_micropython_globals, mp_module_micropython_globals_table);
Expand Down
5 changes: 5 additions & 0 deletions py/mpconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,11 @@ typedef double mp_float_t;
#define MICROPY_PY_MICROPYTHON_HEAP_LOCKED (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING)
#endif

// Whether to provide "micropython.memmove" and "micropython.memset" functions
#ifndef MICROPY_ENABLE_MEM_FUNCTIONS
#define MICROPY_ENABLE_MEM_FUNCTIONS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES)
#endif

// Whether to provide "array" module. Note that large chunk of the
// underlying code is shared with "bytearray" builtin type, so to
// get real savings, it should be disabled too.
Expand Down
14 changes: 14 additions & 0 deletions tests/internal_bench/set_bytearray-1-naive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bench


def test(num):
buf = bytearray(16)
c = b"U"[0]
i = 0
while i < 20000000:
for n in range(len(buf)):
buf[n] = c
i += 1


bench.run(test)
16 changes: 16 additions & 0 deletions tests/internal_bench/set_bytearray-2-naive-while.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import bench


def test(num):
buf = bytearray(16)
c = b"U"[0]
i = 0
while i < 20000000:
n = 0 # compared to -1-naive.py, eliminate the for loop
while n < 16:
buf[n] = c
n += 1
i += 1


bench.run(test)
12 changes: 12 additions & 0 deletions tests/internal_bench/set_bytearray-3-copy_bytes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import bench


def test(num):
buf = bytearray(16)
i = 0
while i < 20000000:
buf[:] = b"UUUUUUUUUUUUUUUU"
i += 1


bench.run(test)
15 changes: 15 additions & 0 deletions tests/internal_bench/set_bytearray-4-memset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import bench

from micropython import memset


def test(num):
buf = bytearray(16)
c = b"U"[0]
i = 0
while i < 20000000:
memset(buf, 0, c)
i += 1


bench.run(test)
14 changes: 14 additions & 0 deletions tests/internal_bench/slice_copy-1-lvalue_start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bench


def test(num):
buf = bytearray(16)
a = b"\x00\x01\x02\x03\x04\x05\x06\x07"
i = 0
while i < 20000000:
# slice only the starting index of lvalue
buf[8:] = a
i += 1


bench.run(test)
14 changes: 14 additions & 0 deletions tests/internal_bench/slice_copy-2-lvalue_start_end.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bench


def test(num):
buf = bytearray(16)
a = b"\x00\x01\x02\x03\x04\x05\x06\x07"
i = 0
while i < 20000000:
# slice the starting index and length of lvalue
buf[8:16] = a
i += 1


bench.run(test)
14 changes: 14 additions & 0 deletions tests/internal_bench/slice_copy-3-lvalue_rvalue_start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bench


def test(num):
buf = bytearray(16)
a = b"\x00\x01\x02\x03\x04\x05\x06\x07"
i = 0
while i < 20000000:
# slice the starting index of lvalue and rvalue
buf[8:] = a[0:]
i += 1


bench.run(test)
14 changes: 14 additions & 0 deletions tests/internal_bench/slice_copy-4-lvalue_memoryview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bench


def test(num):
buf = memoryview(bytearray(16))
a = b"\x00\x01\x02\x03\x04\x05\x06\x07"
i = 0
while i < 20000000:
# slice the memoryview lvalue
buf[8:] = a
i += 1


bench.run(test)
14 changes: 14 additions & 0 deletions tests/internal_bench/slice_copy-5-lvalue_rvalue_memoryview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bench


def test(num):
buf = memoryview(bytearray(16))
a = memoryview(b"\x00\x01\x02\x03\x04\x05\x06\x07")
i = 0
while i < 20000000:
# slice both the lvalue & rvalue memoryviews
buf[8:] = a[0:]
i += 1


bench.run(test)
15 changes: 15 additions & 0 deletions tests/internal_bench/slice_copy-6-memmove.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import bench

from micropython import memmove


def test(num):
buf = bytearray(16)
a = b"\x00\x01\x02\x03\x04\x05\x06\x07"
i = 0
while i < 20000000:
memmove(buf, 8, a, 0) # implicit 'len'
i += 1


bench.run(test)