summaryrefslogtreecommitdiffstatshomepage
path: root/py
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2017-08-14 12:19:09 +1000
committerDamien George <damien.p.george@gmail.com>2017-08-14 12:19:09 +1000
commitbb254ba0ea89ce60dd6deab94991b2651c00dff3 (patch)
treefa2b66f3a1a9fd4bb90b2c83ce9954099b06fe01 /py
parent5e76ea4affd0bd46e67b456496818803010a2d24 (diff)
parente4e4526954f8bcd88ceb21fe789963bfa710fa4f (diff)
downloadmicropython-bb254ba0ea89ce60dd6deab94991b2651c00dff3.tar.gz
micropython-bb254ba0ea89ce60dd6deab94991b2651c00dff3.zip
Merge tag 'v1.8.3' into parse-bytecode
Many small improvements and additions, with btree support in ESP8266 port This release brings various improvements and additions to the core, extended modules and the ESP8266 port, as well as enhancements to the docs. There is now a "threshold" function in the gc module for the user to configure the garbage collector to run earlier than usual, in order to help reduce fragmentation of the heap. The btree module is now available in the ESP8266 port, and there is improved WebREPL file transfer: get file is now non-blocking (this change requires an update of the client software). py core: - nlrx64.S: prefix mp_thread_get_state with an underscore on Mac - stream: implement 2- and 3-arg write() method as an extension to CPython - gc: implement GC running by allocation threshold, user configurable - fix nlrthumb.c when DEBUG=1 is defined - obj: issue a warning when str and bytes objects are compared - mpconfig.h: fix description for MICROPY_PY_STR_BYTES_CMP_WARN - objstrunicode: str_index_to_ptr: should handle bytes too - objstrunicode: str_index_to_ptr: implement positive indexing properly - stream: implement generic flush() method, in terms of C-level ioctl - objstringio: add MP_STREAM_FLUSH ioctl and flush() method - objstringio: implement MP_STREAM_SEEK ioctl and add seek() method - stream: add adapter methods with POSIX-compatible signatures - mpconfig.h: add MICROPY_STREAMS_POSIX_API setting - py.mk: extra switches to build "embedded" BerkeleyDB BTree lib - mkrules.mk: allow to add more items for "clean" target using CLEAN_EXTRA - objstr: make .partition()/.rpartition() methods configurable - mpconfig.h: define MP_ALWAYSINLINE for reuse extmod: - modussl: rename to modussl_axtls.c, to allow impl using other SSL libs - modussl_axtls: further changes to allow alternative SSL modules - modussl_axtls: add dummy setblocking() method - vfs_fat: implement rmdir() method (aliases to unlink) - modussl_axtls: use mp_stream_close() method - modwebrepl: use mp_stream_close() method - modbtree: check __bt_open() return value for error - modbtree: switch to accepting stream object instead of filename - modbtree: implement __contains__ operation - modwebrepl: factor out "GET" iteration to write_file_chunk() - modwebrepl: make GET_FILE operation non-blocking - modbtree: open(): add option kwargs - modwebsocket: add readline method - modwebsocket: make compatible with non-default object models - modwebsocket: use mp_rom_map_elem_t and friends - modwebrepl: add GET_VER operation to query MicroPython version lib: - axtls: upgrade to axTLS 1.5.4 + MicroPython patchset - axtls: update, fixes esp8266 build - utils/stdout_helpers: move from minimal/uart_extra.c for reuse - embed/abort_: implementation of abort_() function raising uPy exception - berkeley-db-1.xx: switch to "embedded" branch - berkeley-db-1.xx: update, sets default page size to 4096 tests: - add test for extended arguments to stream .write() method - extmod/machine_pinbase: skip if machine.PinBase is not available - bytes_compare: rework test for bytes <-> str comparison - extmod/btree1: close database at the end of test - unicode_subscr.py: detailed test for subscripting unicode strings - run-tests: make "regex'ed .exp" facility available to device tests - io/bytesio_ext: test for .seek()/.flush() on BytesIO - extmod/btree1: tests against in-memory DB (using io.BytesIO) - machine_mem.py: too non-portable, rework as an example for unix port - machine1: revamp to work with unix port (which has "umachine") - basics: bytes/str.partition/rpartition are now optional mpy-cross: - fix Makefile to handle gc-sections linker flags on OS X - use binary file translation mode for creating mpy files on windows - fix mingw and msys2 compilation minimal port: - disable MICROPY_GC_ALLOC_THRESHOLD unix port: - mpthreadport: adjust minimum thread stack, and stack limit check - cache libaxtls.a in local build dir - disable MICROPY_GC_ALLOC_THRESHOLD for minimal build - enable MICROPY_PY_STR_BYTES_CMP_WARN - mpconfigport.h: include stdio.h by default - file: implement MP_STREAM_FLUSH ioctl - file: ioctl(): check that file is open before operations - file: fdfile_ioctl(): fix argument to check_fd_is_open() - file: use generic stream flush() method - enable websocket module - moduselect: allow poll.register(), etc. accept fd-like objects stmhal port: - fix malloc when used with external libraries - make SPI NSS pin definition optional - fix I2C mappings for STM32F429DISC board esp8266 port: - select axTLS for SSL implementation, following recent refactor - moduos: add rmdir() function - cache Xtensa-built libaxtls.a in local build dir - enable MICROPY_PY_STR_BYTES_CMP_WARN - dupterm_task_init() should be called before running _boot.py, etc - _boot.py: set GC alloc threshold to half of heap size - _boot.py: decrease GC alloc threshold to quarter of heap size - modpybuart: fix UART parity setting - axtls_helpers: remove abort_(), now in lib/embed/ - mpconfigport.h: include sys/types.h for POSIX types definitions - esp_mphal: implement libc's errno - enable btree module - eagle.rom.addr.v6.ld: add Enable_QMode symbol from SDK 2.0.0 - make APA102 driver inclusion configurable - makeimg.py: store firmware size as last 4 bytes of padding area - makeimg.py: append md5 hash to the generated binary - modesp: add check_fw() function to check integrity of the firmware - scripts/port_diag.py: include esp.check_fw() call - flashbdev: reserve extra sysparam sector for SDK 2.0.0 compatibility - scripts/inisetup: add commented-out call to esp.osdebug(None) - modmachine: implement idle() function - esp_mphal.h: add mp_hal_ticks_cpu() for reuse - modutime: actually implement ticks_cpu() - modmachine: implement dummy sleep() function - tutorial/intro: reword para abou -fm dio switch - modules/flashbdev: start filesystem at 0x90000 - esp8266.ld: increase firmware image size to 0x90000 (576K) docs: - add DHT to ESP8266 Quick Ref and Tutorial - fix some spelling mistakes - array: document array module - library/index: include array module in ToC - esp8266/intro: rename to "Getting started" from "Introduction" - esp8266/intro: add troubleshooting section - esp8266/quickref: link to installation instructions - esp8266/tutorial/intro: add anchor for link from quickeref - esp8266/intro: focus on hazards of unearthed power wrt electronics - uio: mention seek()/flush() support for io.BytesIO misc: - logo/1bit-logo A black & white version of the logo examples: - http_server*: update for buffered-like streams (read line by line) - embedding: example for embedding MicroPython in an app - embedding: add README - http_client*: be sure to close socket - network: split recv- and read-based HTTP servers qemu-arm: - enable gcc LTO option for nlrthumb.c
Diffstat (limited to 'py')
-rw-r--r--py/gc.c22
-rw-r--r--py/mkrules.mk2
-rw-r--r--py/modgc.c22
-rw-r--r--py/mpconfig.h27
-rw-r--r--py/mpstate.h5
-rw-r--r--py/nlrthumb.c4
-rw-r--r--py/nlrx64.S8
-rw-r--r--py/obj.c8
-rw-r--r--py/objstr.c10
-rw-r--r--py/objstringio.c40
-rw-r--r--py/objstrunicode.c30
-rw-r--r--py/py.mk12
-rw-r--r--py/stream.c91
-rw-r--r--py/stream.h15
14 files changed, 266 insertions, 30 deletions
diff --git a/py/gc.c b/py/gc.c
index 1c1865cdb4..97868c07f8 100644
--- a/py/gc.c
+++ b/py/gc.c
@@ -152,6 +152,12 @@ void gc_init(void *start, void *end) {
// allow auto collection
MP_STATE_MEM(gc_auto_collect_enabled) = 1;
+ #if MICROPY_GC_ALLOC_THRESHOLD
+ // by default, maxuint for gc threshold, effectively turning gc-by-threshold off
+ MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1;
+ MP_STATE_MEM(gc_alloc_amount) = 0;
+ #endif
+
#if MICROPY_PY_THREAD
mp_thread_mutex_init(&MP_STATE_MEM(gc_mutex));
#endif
@@ -294,6 +300,9 @@ STATIC void gc_sweep(void) {
void gc_collect_start(void) {
GC_ENTER();
MP_STATE_MEM(gc_lock_depth)++;
+ #if MICROPY_GC_ALLOC_THRESHOLD
+ MP_STATE_MEM(gc_alloc_amount) = 0;
+ #endif
MP_STATE_MEM(gc_stack_overflow) = 0;
MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack);
// Trace root pointers. This relies on the root pointers being organised
@@ -405,6 +414,15 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) {
size_t start_block;
size_t n_free = 0;
int collected = !MP_STATE_MEM(gc_auto_collect_enabled);
+
+ #if MICROPY_GC_ALLOC_THRESHOLD
+ if (!collected && MP_STATE_MEM(gc_alloc_amount) >= MP_STATE_MEM(gc_alloc_threshold)) {
+ GC_EXIT();
+ gc_collect();
+ GC_ENTER();
+ }
+ #endif
+
for (;;) {
// look for a run of n_blocks available blocks
@@ -456,6 +474,10 @@ found:
void *ret_ptr = (void*)(MP_STATE_MEM(gc_pool_start) + start_block * BYTES_PER_BLOCK);
DEBUG_printf("gc_alloc(%p)\n", ret_ptr);
+ #if MICROPY_GC_ALLOC_THRESHOLD
+ MP_STATE_MEM(gc_alloc_amount) += n_blocks;
+ #endif
+
GC_EXIT();
// zero out the additional bytes of the newly allocated blocks
diff --git a/py/mkrules.mk b/py/mkrules.mk
index b77f8d600f..3cf0e30584 100644
--- a/py/mkrules.mk
+++ b/py/mkrules.mk
@@ -127,7 +127,7 @@ lib: $(OBJ)
$(AR) rcs libmicropython.a $^
clean:
- $(RM) -rf $(BUILD)
+ $(RM) -rf $(BUILD) $(CLEAN_EXTRA)
.PHONY: clean
print-cfg:
diff --git a/py/modgc.c b/py/modgc.c
index d68ff7e6c0..976fb89980 100644
--- a/py/modgc.c
+++ b/py/modgc.c
@@ -83,6 +83,25 @@ STATIC mp_obj_t gc_mem_alloc(void) {
}
MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_alloc_obj, gc_mem_alloc);
+#if MICROPY_GC_ALLOC_THRESHOLD
+STATIC mp_obj_t gc_threshold(size_t n_args, const mp_obj_t *args) {
+ if (n_args == 0) {
+ if (MP_STATE_MEM(gc_alloc_threshold) == (size_t)-1) {
+ return MP_OBJ_NEW_SMALL_INT(-1);
+ }
+ return mp_obj_new_int(MP_STATE_MEM(gc_alloc_threshold) * MICROPY_BYTES_PER_GC_BLOCK);
+ }
+ mp_int_t val = mp_obj_get_int(args[0]);
+ if (val < 0) {
+ MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1;
+ } else {
+ MP_STATE_MEM(gc_alloc_threshold) = val / MICROPY_BYTES_PER_GC_BLOCK;
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gc_threshold_obj, 0, 1, gc_threshold);
+#endif
+
STATIC const mp_rom_map_elem_t mp_module_gc_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gc) },
{ MP_ROM_QSTR(MP_QSTR_collect), MP_ROM_PTR(&gc_collect_obj) },
@@ -91,6 +110,9 @@ STATIC const mp_rom_map_elem_t mp_module_gc_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_isenabled), MP_ROM_PTR(&gc_isenabled_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem_free), MP_ROM_PTR(&gc_mem_free_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem_alloc), MP_ROM_PTR(&gc_mem_alloc_obj) },
+ #if MICROPY_GC_ALLOC_THRESHOLD
+ { MP_ROM_QSTR(MP_QSTR_threshold), MP_ROM_PTR(&gc_threshold_obj) },
+ #endif
};
STATIC MP_DEFINE_CONST_DICT(mp_module_gc_globals, mp_module_gc_globals_table);
diff --git a/py/mpconfig.h b/py/mpconfig.h
index aec5d40826..3808df7430 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -107,6 +107,12 @@
#define MICROPY_ALLOC_GC_STACK_SIZE (64)
#endif
+// Support automatic GC when reaching allocation threshold,
+// configurable by gc.threshold().
+#ifndef MICROPY_GC_ALLOC_THRESHOLD
+#define MICROPY_GC_ALLOC_THRESHOLD (1)
+#endif
+
// Number of bytes to allocate initially when creating new chunks to store
// interned string data. Smaller numbers lead to more chunks being needed
// and more wastage at the end of the chunk. Larger numbers lead to wasted
@@ -516,6 +522,12 @@ typedef double mp_float_t;
#define MICROPY_STREAMS_NON_BLOCK (0)
#endif
+// Whether to provide stream functions with POSIX-like signatures
+// (useful for porting existing libraries to MicroPython).
+#ifndef MICROPY_STREAMS_POSIX_API
+#define MICROPY_STREAMS_POSIX_API (0)
+#endif
+
// Whether to call __init__ when importing builtin modules for the first time
#ifndef MICROPY_MODULE_BUILTIN_INIT
#define MICROPY_MODULE_BUILTIN_INIT (0)
@@ -584,6 +596,11 @@ typedef double mp_float_t;
#define MICROPY_PY_ASYNC_AWAIT (1)
#endif
+// Issue a warning when comparing str and bytes objects
+#ifndef MICROPY_PY_STR_BYTES_CMP_WARN
+#define MICROPY_PY_STR_BYTES_CMP_WARN (0)
+#endif
+
// Whether str object is proper unicode
#ifndef MICROPY_PY_BUILTINS_STR_UNICODE
#define MICROPY_PY_BUILTINS_STR_UNICODE (0)
@@ -594,6 +611,11 @@ typedef double mp_float_t;
#define MICROPY_PY_BUILTINS_STR_CENTER (0)
#endif
+// Whether str.partition()/str.rpartition() method provided
+#ifndef MICROPY_PY_BUILTINS_STR_PARTITION
+#define MICROPY_PY_BUILTINS_STR_PARTITION (0)
+#endif
+
// Whether str.splitlines() method provided
#ifndef MICROPY_PY_BUILTINS_STR_SPLITLINES
#define MICROPY_PY_BUILTINS_STR_SPLITLINES (0)
@@ -1051,6 +1073,11 @@ typedef double mp_float_t;
#define MP_NOINLINE __attribute__((noinline))
#endif
+// Modifier for functions which should be always inlined
+#ifndef MP_ALWAYSINLINE
+#define MP_ALWAYSINLINE __attribute__((always_inline))
+#endif
+
// Condition is likely to be true, to help branch prediction
#ifndef MP_LIKELY
#define MP_LIKELY(x) __builtin_expect((x), 1)
diff --git a/py/mpstate.h b/py/mpstate.h
index 281795773f..439ed66066 100644
--- a/py/mpstate.h
+++ b/py/mpstate.h
@@ -76,6 +76,11 @@ typedef struct _mp_state_mem_t {
// you can still allocate/free memory and also explicitly call gc_collect.
uint16_t gc_auto_collect_enabled;
+ #if MICROPY_GC_ALLOC_THRESHOLD
+ size_t gc_alloc_amount;
+ size_t gc_alloc_threshold;
+ #endif
+
size_t gc_last_free_atb_index;
#if MICROPY_PY_GC_COLLECT_RETVAL
diff --git a/py/nlrthumb.c b/py/nlrthumb.c
index a61c73c036..08a71ac7d6 100644
--- a/py/nlrthumb.c
+++ b/py/nlrthumb.c
@@ -29,6 +29,8 @@
#if (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__))
+#undef nlr_push
+
// We only need the functions here if we are on arm/thumb, and we are not
// using setjmp/longjmp.
//
@@ -71,7 +73,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
return 0; // needed to silence compiler warning
}
-unsigned int nlr_push_tail(nlr_buf_t *nlr) {
+__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
nlr->prev = *top;
*top = nlr;
diff --git a/py/nlrx64.S b/py/nlrx64.S
index ad2b66fdb2..caea35de2b 100644
--- a/py/nlrx64.S
+++ b/py/nlrx64.S
@@ -37,8 +37,10 @@
#if defined(__APPLE__) && defined(__MACH__)
#define NLR_TOP (_mp_state_ctx + NLR_TOP_OFFSET)
+#define MP_THREAD_GET_STATE _mp_thread_get_state
#else
#define NLR_TOP (mp_state_ctx + NLR_TOP_OFFSET)
+#define MP_THREAD_GET_STATE mp_thread_get_state
#endif
// offset of nlr_top within mp_state_thread_t structure
@@ -87,7 +89,7 @@ _nlr_push:
movq %rdi, NLR_TOP(%rip) # stor new nlr_buf (to make linked list)
#else
movq %rdi, %rbp # since we make a call, must save rdi in rbp
- callq mp_thread_get_state # get mp_state_thread ptr into rax
+ callq MP_THREAD_GET_STATE # get mp_state_thread ptr into rax
movq NLR_TOP_TH_OFF(%rax), %rsi # get thread.nlr_top (last nlr_buf)
movq %rsi, (%rbp) # store it
movq %rbp, NLR_TOP_TH_OFF(%rax) # store new nlr_buf (to make linked list)
@@ -117,7 +119,7 @@ _nlr_pop:
movq (%rax), %rax # load prev nlr_buf
movq %rax, NLR_TOP(%rip) # store prev nlr_buf (to unlink list)
#else
- callq mp_thread_get_state # get mp_state_thread ptr into rax
+ callq MP_THREAD_GET_STATE # get mp_state_thread ptr into rax
movq NLR_TOP_TH_OFF(%rax), %rdi # get thread.nlr_top (last nlr_buf)
movq (%rdi), %rdi # load prev nlr_buf
movq %rdi, NLR_TOP_TH_OFF(%rax) # store prev nlr_buf (to unlink list)
@@ -150,7 +152,7 @@ nlr_jump:
movq %rax, NLR_TOP(%rip) # store prev nlr_buf (to unlink list)
#else
movq %rdi, %rbp # put return value in rbp
- callq mp_thread_get_state # get thread ptr in rax
+ callq MP_THREAD_GET_STATE # get thread ptr in rax
movq %rax, %rsi # put thread ptr in rsi
movq %rbp, %rax # put return value to rax (for je .fail)
movq NLR_TOP_TH_OFF(%rsi), %rdi # get thread.nlr_top in rdi
diff --git a/py/obj.c b/py/obj.c
index 9efa0f05ae..d6ce3dae6a 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -192,10 +192,16 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) {
return mp_obj_str_equal(o1, o2);
} else {
// a string is never equal to anything else
- return false;
+ goto str_cmp_err;
}
} else if (MP_OBJ_IS_STR(o2)) {
// o1 is not a string (else caught above), so the objects are not equal
+ str_cmp_err:
+ #if MICROPY_PY_STR_BYTES_CMP_WARN
+ if (MP_OBJ_IS_TYPE(o1, &mp_type_bytes) || MP_OBJ_IS_TYPE(o2, &mp_type_bytes)) {
+ mp_warning("Comparison between bytes and str");
+ }
+ #endif
return false;
}
diff --git a/py/objstr.c b/py/objstr.c
index e51c371f7b..a6ee617c03 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -1683,6 +1683,7 @@ STATIC mp_obj_t str_count(size_t n_args, const mp_obj_t *args) {
return MP_OBJ_NEW_SMALL_INT(num_occurrences);
}
+#if MICROPY_PY_BUILTINS_STR_PARTITION
STATIC mp_obj_t str_partitioner(mp_obj_t self_in, mp_obj_t arg, mp_int_t direction) {
assert(MP_OBJ_IS_STR_OR_BYTES(self_in));
mp_obj_type_t *self_type = mp_obj_get_type(self_in);
@@ -1732,6 +1733,7 @@ STATIC mp_obj_t str_partition(mp_obj_t self_in, mp_obj_t arg) {
STATIC mp_obj_t str_rpartition(mp_obj_t self_in, mp_obj_t arg) {
return str_partitioner(self_in, arg, -1);
}
+#endif
// Supposedly not too critical operations, so optimize for code size
STATIC mp_obj_t str_caseconv(unichar (*op)(unichar), mp_obj_t self_in) {
@@ -1875,8 +1877,10 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rstrip_obj, 1, 2, str_rstrip);
MP_DEFINE_CONST_FUN_OBJ_KW(str_format_obj, 1, mp_obj_str_format);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_replace_obj, 3, 4, str_replace);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_count_obj, 2, 4, str_count);
+#if MICROPY_PY_BUILTINS_STR_PARTITION
MP_DEFINE_CONST_FUN_OBJ_2(str_partition_obj, str_partition);
MP_DEFINE_CONST_FUN_OBJ_2(str_rpartition_obj, str_rpartition);
+#endif
MP_DEFINE_CONST_FUN_OBJ_1(str_lower_obj, str_lower);
MP_DEFINE_CONST_FUN_OBJ_1(str_upper_obj, str_upper);
MP_DEFINE_CONST_FUN_OBJ_1(str_isspace_obj, str_isspace);
@@ -1915,11 +1919,13 @@ STATIC const mp_rom_map_elem_t str8_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_format), MP_ROM_PTR(&str_format_obj) },
{ MP_ROM_QSTR(MP_QSTR_replace), MP_ROM_PTR(&str_replace_obj) },
{ MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&str_count_obj) },
+ #if MICROPY_PY_BUILTINS_STR_PARTITION
{ MP_ROM_QSTR(MP_QSTR_partition), MP_ROM_PTR(&str_partition_obj) },
{ MP_ROM_QSTR(MP_QSTR_rpartition), MP_ROM_PTR(&str_rpartition_obj) },
-#if MICROPY_PY_BUILTINS_STR_CENTER
+ #endif
+ #if MICROPY_PY_BUILTINS_STR_CENTER
{ MP_ROM_QSTR(MP_QSTR_center), MP_ROM_PTR(&str_center_obj) },
-#endif
+ #endif
{ MP_ROM_QSTR(MP_QSTR_lower), MP_ROM_PTR(&str_lower_obj) },
{ MP_ROM_QSTR(MP_QSTR_upper), MP_ROM_PTR(&str_upper_obj) },
{ MP_ROM_QSTR(MP_QSTR_isspace), MP_ROM_PTR(&str_isspace_obj) },
diff --git a/py/objstringio.c b/py/objstringio.c
index abd4e835e8..eb2e516bb3 100644
--- a/py/objstringio.c
+++ b/py/objstringio.c
@@ -75,13 +75,18 @@ STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size,
(void)errcode;
mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
check_stringio_is_open(o);
- mp_uint_t remaining = o->vstr->alloc - o->pos;
- if (size > remaining) {
+ mp_int_t remaining = o->vstr->alloc - o->pos;
+ mp_uint_t org_len = o->vstr->len;
+ if ((mp_int_t)size > remaining) {
// Take all what's already allocated...
o->vstr->len = o->vstr->alloc;
// ... and add more
vstr_add_len(o->vstr, size - remaining);
}
+ // If there was a seek past EOF, clear the hole
+ if (o->pos > org_len) {
+ memset(o->vstr->buf + org_len, 0, o->pos - org_len);
+ }
memcpy(o->vstr->buf + o->pos, buf, size);
o->pos += size;
if (o->pos > o->vstr->len) {
@@ -90,6 +95,33 @@ STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size,
return size;
}
+STATIC mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+ (void)errcode;
+ mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
+ switch (request) {
+ case MP_STREAM_SEEK: {
+ struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)arg;
+ mp_uint_t ref = 0;
+ switch (s->whence) {
+ case 1: // SEEK_CUR
+ ref = o->pos;
+ break;
+ case 2: // SEEK_END
+ ref = o->vstr->len;
+ break;
+ }
+ o->pos = ref + s->offset;
+ s->offset = o->pos;
+ return 0;
+ }
+ case MP_STREAM_FLUSH:
+ return 0;
+ default:
+ *errcode = MP_EINVAL;
+ return MP_STREAM_ERROR;
+ }
+}
+
#define STREAM_TO_CONTENT_TYPE(o) (((o)->base.type == &mp_type_stringio) ? &mp_type_str : &mp_type_bytes)
STATIC mp_obj_t stringio_getvalue(mp_obj_t self_in) {
@@ -148,6 +180,8 @@ STATIC const mp_rom_map_elem_t stringio_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
+ { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
+ { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&stringio_close_obj) },
{ MP_ROM_QSTR(MP_QSTR_getvalue), MP_ROM_PTR(&stringio_getvalue_obj) },
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
@@ -159,12 +193,14 @@ STATIC MP_DEFINE_CONST_DICT(stringio_locals_dict, stringio_locals_dict_table);
STATIC const mp_stream_p_t stringio_stream_p = {
.read = stringio_read,
.write = stringio_write,
+ .ioctl = stringio_ioctl,
.is_text = true,
};
STATIC const mp_stream_p_t bytesio_stream_p = {
.read = stringio_read,
.write = stringio_write,
+ .ioctl = stringio_ioctl,
};
const mp_obj_type_t mp_type_stringio = {
diff --git a/py/objstrunicode.c b/py/objstrunicode.c
index c6c775d109..8444a26892 100644
--- a/py/objstrunicode.c
+++ b/py/objstrunicode.c
@@ -116,7 +116,14 @@ STATIC mp_obj_t uni_unary_op(mp_uint_t op, mp_obj_t self_in) {
// be capped to the first/last character of the string, depending on is_slice.
const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, size_t self_len,
mp_obj_t index, bool is_slice) {
- (void)type;
+ // All str functions also handle bytes objects, and they call str_index_to_ptr(),
+ // so it must handle bytes.
+ if (type == &mp_type_bytes) {
+ // Taken from objstr.c:str_index_to_ptr()
+ mp_uint_t index_val = mp_get_index(type, self_len, index, is_slice);
+ return self_data + index_val;
+ }
+
mp_int_t i;
// Copied from mp_get_index; I don't want bounds checking, just give me
// the integer as-is. (I can't bounds-check without scanning the whole
@@ -142,26 +149,29 @@ const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, s
}
}
++s;
- } else if (!i) {
- return self_data; // Shortcut - str[0] is its base pointer
} else {
// Positive indexing, correspondingly, counts from the start of the string.
// It's assumed that negative indexing will generally be used with small
// absolute values (eg str[-1], not str[-1000000]), which means it'll be
// more efficient this way.
- for (s = self_data; true; ++s) {
+ s = self_data;
+ while (1) {
+ // First check out-of-bounds
if (s >= top) {
if (is_slice) {
return top;
}
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_IndexError, "string index out of range"));
}
+ // Then check completion
+ if (i-- == 0) {
+ break;
+ }
+ // Then skip UTF-8 char
+ ++s;
while (UTF8_IS_CONT(*s)) {
++s;
}
- if (!i--) {
- return s;
- }
}
}
return s;
@@ -236,11 +246,13 @@ STATIC const mp_rom_map_elem_t struni_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_format), MP_ROM_PTR(&str_format_obj) },
{ MP_ROM_QSTR(MP_QSTR_replace), MP_ROM_PTR(&str_replace_obj) },
{ MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&str_count_obj) },
+ #if MICROPY_PY_BUILTINS_STR_PARTITION
{ MP_ROM_QSTR(MP_QSTR_partition), MP_ROM_PTR(&str_partition_obj) },
{ MP_ROM_QSTR(MP_QSTR_rpartition), MP_ROM_PTR(&str_rpartition_obj) },
-#if MICROPY_PY_BUILTINS_STR_CENTER
+ #endif
+ #if MICROPY_PY_BUILTINS_STR_CENTER
{ MP_ROM_QSTR(MP_QSTR_center), MP_ROM_PTR(&str_center_obj) },
-#endif
+ #endif
{ MP_ROM_QSTR(MP_QSTR_lower), MP_ROM_PTR(&str_lower_obj) },
{ MP_ROM_QSTR(MP_QSTR_upper), MP_ROM_PTR(&str_upper_obj) },
{ MP_ROM_QSTR(MP_QSTR_isspace), MP_ROM_PTR(&str_isspace_obj) },
diff --git a/py/py.mk b/py/py.mk
index 79df48094b..aba173a908 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -20,8 +20,11 @@ INC += -I../lib
INC += -I../lib/netutils
ifeq ($(MICROPY_PY_USSL),1)
-CFLAGS_MOD += -DMICROPY_PY_USSL=1 -I../lib/axtls/ssl -I../lib/axtls/crypto -I../lib/axtls/config
-LDFLAGS_MOD += -L../lib/axtls/_stage -laxtls
+CFLAGS_MOD += -DMICROPY_PY_USSL=1
+ifeq ($(MICROPY_SSL_AXTLS),1)
+CFLAGS_MOD += -DMICROPY_SSL_AXTLS=1 -I../lib/axtls/ssl -I../lib/axtls/crypto -I../lib/axtls/config
+LDFLAGS_MOD += -Lbuild -laxtls
+endif
endif
#ifeq ($(MICROPY_PY_LWIP),1)
@@ -66,7 +69,7 @@ endif
ifeq ($(MICROPY_PY_BTREE),1)
BTREE_DIR = lib/berkeley-db-1.xx
-CFLAGS_MOD += -D__DBINTERFACE_PRIVATE=1
+CFLAGS_MOD += -D__DBINTERFACE_PRIVATE=1 -Dmpool_error=printf -Dabort=abort_ -Dvirt_fd_t=mp_obj_t "-DVIRT_FD_T_HEADER=<py/obj.h>"
INC += -I../$(BTREE_DIR)/PORT/include
SRC_MOD += extmod/modbtree.c
SRC_MOD += $(addprefix $(BTREE_DIR)/,\
@@ -205,7 +208,7 @@ PY_O_BASENAME = \
../extmod/machine_pinbase.o \
../extmod/machine_pulse.o \
../extmod/machine_i2c.o \
- ../extmod/modussl.o \
+ ../extmod/modussl_axtls.o \
../extmod/modurandom.o \
../extmod/modwebsocket.o \
../extmod/modwebrepl.o \
@@ -218,6 +221,7 @@ PY_O_BASENAME = \
../extmod/vfs_fat_lexer.o \
../extmod/vfs_fat_misc.o \
../extmod/moduos_dupterm.o \
+ ../lib/embed/abort_.o \
# prepend the build destination prefix to the py object files
PY_O = $(addprefix $(PY_BUILD)/, $(PY_O_BASENAME))
diff --git a/py/stream.c b/py/stream.c
index 4fcc151dca..473eb96904 100644
--- a/py/stream.c
+++ b/py/stream.c
@@ -267,12 +267,24 @@ void mp_stream_write_adaptor(void *self, const char *buf, size_t len) {
mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len, MP_STREAM_RW_WRITE);
}
-STATIC mp_obj_t stream_write_method(mp_obj_t self_in, mp_obj_t arg) {
+STATIC mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) {
mp_buffer_info_t bufinfo;
- mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
- return mp_stream_write(self_in, bufinfo.buf, bufinfo.len, MP_STREAM_RW_WRITE);
+ mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
+ size_t max_len = (size_t)-1;
+ size_t off = 0;
+ if (n_args == 3) {
+ max_len = mp_obj_get_int_truncated(args[2]);
+ } else if (n_args == 4) {
+ off = mp_obj_get_int_truncated(args[2]);
+ max_len = mp_obj_get_int_truncated(args[3]);
+ if (off > bufinfo.len) {
+ off = bufinfo.len;
+ }
+ }
+ bufinfo.len -= off;
+ return mp_stream_write(args[0], (byte*)bufinfo.buf + off, MIN(bufinfo.len, max_len), MP_STREAM_RW_WRITE);
}
-MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_write_obj, stream_write_method);
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj, 2, 4, stream_write_method);
STATIC mp_obj_t stream_write1_method(mp_obj_t self_in, mp_obj_t arg) {
mp_buffer_info_t bufinfo;
@@ -465,6 +477,17 @@ STATIC mp_obj_t stream_tell(mp_obj_t self) {
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_tell_obj, stream_tell);
+STATIC mp_obj_t stream_flush(mp_obj_t self) {
+ const mp_stream_p_t *stream_p = mp_get_stream_raise(self, MP_STREAM_OP_IOCTL);
+ int error;
+ mp_uint_t res = stream_p->ioctl(self, MP_STREAM_FLUSH, 0, &error);
+ if (res == MP_STREAM_ERROR) {
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error)));
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_flush_obj, stream_flush);
+
STATIC mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) {
const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_IOCTL);
@@ -487,3 +510,63 @@ STATIC mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) {
return mp_obj_new_int(res);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj, 2, 3, stream_ioctl);
+
+#if MICROPY_STREAMS_POSIX_API
+/*
+ * POSIX-like functions
+ *
+ * These functions have POSIX-compatible signature (except for "void *stream"
+ * first argument instead of "int fd"). They are useful to port existing
+ * POSIX-compatible software to work with MicroPython streams.
+ */
+
+// errno-like variable. If any of the functions below returned with error
+// status, this variable will contain error no.
+int mp_stream_errno;
+
+ssize_t mp_stream_posix_write(mp_obj_t stream, const void *buf, size_t len) {
+ mp_obj_base_t* o = (mp_obj_base_t*)MP_OBJ_TO_PTR(stream);
+ const mp_stream_p_t *stream_p = o->type->protocol;
+ mp_uint_t out_sz = stream_p->write(stream, buf, len, &mp_stream_errno);
+ if (out_sz == MP_STREAM_ERROR) {
+ return -1;
+ } else {
+ return out_sz;
+ }
+}
+
+ssize_t mp_stream_posix_read(mp_obj_t stream, void *buf, size_t len) {
+ mp_obj_base_t* o = (mp_obj_base_t*)MP_OBJ_TO_PTR(stream);
+ const mp_stream_p_t *stream_p = o->type->protocol;
+ mp_uint_t out_sz = stream_p->read(stream, buf, len, &mp_stream_errno);
+ if (out_sz == MP_STREAM_ERROR) {
+ return -1;
+ } else {
+ return out_sz;
+ }
+}
+
+off_t mp_stream_posix_lseek(mp_obj_t stream, off_t offset, int whence) {
+ const mp_obj_base_t* o = (mp_obj_base_t*)MP_OBJ_TO_PTR(stream);
+ const mp_stream_p_t *stream_p = o->type->protocol;
+ struct mp_stream_seek_t seek_s;
+ seek_s.offset = offset;
+ seek_s.whence = whence;
+ mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &mp_stream_errno);
+ if (res == MP_STREAM_ERROR) {
+ return -1;
+ }
+ return seek_s.offset;
+}
+
+int mp_stream_posix_fsync(mp_obj_t stream) {
+ mp_obj_base_t* o = (mp_obj_base_t*)MP_OBJ_TO_PTR(stream);
+ const mp_stream_p_t *stream_p = o->type->protocol;
+ mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_FLUSH, 0, &mp_stream_errno);
+ if (res == MP_STREAM_ERROR) {
+ return -1;
+ }
+ return res;
+}
+
+#endif
diff --git a/py/stream.h b/py/stream.h
index b0f45e2f02..33d85e823c 100644
--- a/py/stream.h
+++ b/py/stream.h
@@ -27,6 +27,7 @@
#define __MICROPY_INCLUDED_PY_STREAM_H__
#include "py/obj.h"
+#include "py/mperrno.h"
#define MP_STREAM_ERROR ((mp_uint_t)-1)
@@ -57,6 +58,7 @@ MP_DECLARE_CONST_FUN_OBJ(mp_stream_write_obj);
MP_DECLARE_CONST_FUN_OBJ(mp_stream_write1_obj);
MP_DECLARE_CONST_FUN_OBJ(mp_stream_seek_obj);
MP_DECLARE_CONST_FUN_OBJ(mp_stream_tell_obj);
+MP_DECLARE_CONST_FUN_OBJ(mp_stream_flush_obj);
MP_DECLARE_CONST_FUN_OBJ(mp_stream_ioctl_obj);
// these are for mp_get_stream_raise and can be or'd together
@@ -80,13 +82,20 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf, mp_uint_t size, int *errcode,
#define mp_stream_write_exactly(stream, buf, size, err) mp_stream_rw(stream, (byte*)buf, size, err, MP_STREAM_RW_WRITE)
#define mp_stream_read_exactly(stream, buf, size, err) mp_stream_rw(stream, buf, size, err, MP_STREAM_RW_READ)
+void mp_stream_write_adaptor(void *self, const char *buf, size_t len);
+
+#if MICROPY_STREAMS_POSIX_API
+// Functions with POSIX-compatible signatures
+ssize_t mp_stream_posix_write(mp_obj_t stream, const void *buf, size_t len);
+ssize_t mp_stream_posix_read(mp_obj_t stream, void *buf, size_t len);
+off_t mp_stream_posix_lseek(mp_obj_t stream, off_t offset, int whence);
+int mp_stream_posix_fsync(mp_obj_t stream);
+#endif
+
#if MICROPY_STREAMS_NON_BLOCK
-// TODO: This is POSIX-specific (but then POSIX is the only real thing,
-// and anything else just emulates it, right?)
#define mp_is_nonblocking_error(errno) ((errno) == EAGAIN || (errno) == EWOULDBLOCK)
#else
#define mp_is_nonblocking_error(errno) (0)
#endif
#endif // __MICROPY_INCLUDED_PY_STREAM_H__
-void mp_stream_write_adaptor(void *self, const char *buf, size_t len);