From 06b6d9f0490a054101d34c02b267946e261b7464 Mon Sep 17 00:00:00 2001 From: Lyon Date: Thu, 20 Jul 2023 11:07:41 +0800 Subject: [PATCH] improve err report and add struct model(not test) --- package/struct/_struct.c | 255 +++++++++++ package/struct/_struct.pyi | 0 port/linux/package/pikascript/_struct.pyi | 0 .../pikascript-lib/struct/_struct.c | 255 +++++++++++ src/PikaObj.h | 23 +- src/PikaPlatform.c | 36 +- src/PikaPlatform.h | 29 +- src/dataArgs.h | 8 + src/pika_adapter_mpy.h | 422 +++++++++++++++++- 9 files changed, 976 insertions(+), 52 deletions(-) create mode 100644 package/struct/_struct.c create mode 100644 package/struct/_struct.pyi create mode 100644 port/linux/package/pikascript/_struct.pyi create mode 100644 port/linux/package/pikascript/pikascript-lib/struct/_struct.c diff --git a/package/struct/_struct.c b/package/struct/_struct.c new file mode 100644 index 000000000..5554c5f80 --- /dev/null +++ b/package/struct/_struct.c @@ -0,0 +1,255 @@ +/* + * This file is part of the PikaPython project, https://pikapython.com + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2023 Lyon + * + * 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 "_struct.h" +#include "pika_adapter_mpy.h" + +#define _SKIP_COMPILE 1 + +#ifndef _SKIP_COMPILE + +/* + This module implements most of character typecodes from CPython, with + some extensions: + + O - (Pointer to) an arbitrary Python object. This is useful for callback + data, etc. Note that you must keep reference to passed object in + your Python application, otherwise it may be garbage-collected, + and then when you get back this value from callback it may be + invalid (and lead to crash). + S - Pointer to a string (returned as a Python string). Note the + difference from "Ns", - the latter says "in this place of structure + is character data of up to N bytes length", while "S" means + "in this place of a structure is a pointer to zero-terminated + character data". + */ + +STATIC char get_fmt_type(const char** fmt) { + char t = **fmt; + switch (t) { + case '!': + t = '>'; + break; + case '@': + case '=': + case '<': + case '>': + break; + default: + return '@'; + } + // Skip type char + (*fmt)++; + return t; +} + +STATIC mp_uint_t get_fmt_num(const char** p) { + const char* num = *p; + uint len = 1; + while (unichar_isdigit(*++num)) { + len++; + } + char* buff = pika_platform_malloc(len + 1); + pika_platform_memset(buff, 0, len + 1); + pika_platform_memcpy(buff, *p, len); + mp_uint_t val = (mp_uint_t)fast_atoi((char*)num); + *p = num; + return val; +} + +STATIC size_t calc_size_items(const char* fmt, size_t* total_sz) { + char fmt_type = get_fmt_type(&fmt); + size_t total_cnt = 0; + size_t size; + for (size = 0; *fmt; fmt++) { + mp_uint_t cnt = 1; + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + + if (*fmt == 's') { + total_cnt += 1; + size += cnt; + } else { + total_cnt += cnt; + size_t align; + size_t sz = mp_binary_get_size(fmt_type, *fmt, &align); + while (cnt--) { + // Apply alignment + size = (size + align - 1) & ~(align - 1); + size += sz; + } + } + } + *total_sz = size; + return total_cnt; +} + +STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) { + const char* fmt = mp_obj_str_get_str(fmt_in); + size_t size; + calc_size_items(fmt, &size); + return MP_OBJ_NEW_SMALL_INT(size); +} + +STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t* args) { + // unpack requires that the buffer be exactly the right size. + // unpack_from requires that the buffer be "big enough". + // Since we implement unpack and unpack_from using the same function + // we relax the "exact" requirement, and only implement "big enough". + const char* fmt = mp_obj_str_get_str((Arg*)args[0]); + size_t total_sz; + size_t num_items = calc_size_items(fmt, &total_sz); + char fmt_type = get_fmt_type(&fmt); + mp_obj_tuple_t* res = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_items, NULL)); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise((Arg*)args[1], &bufinfo, MP_BUFFER_READ); + byte* p = bufinfo.buf; + byte* end_p = &p[bufinfo.len]; + mp_int_t offset = 0; + + if (n_args > 2) { + // offset arg provided + offset = mp_obj_get_int((Arg*)args[2]); + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = bufinfo.len + offset; + if (offset < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + } + p += offset; + } + byte* p_base = p; + + // Check that the input buffer is big enough to unpack all the values + if (p + total_sz > end_p) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + + for (size_t i = 0; i < num_items;) { + mp_uint_t cnt = 1; + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + mp_obj_t item; + if (*fmt == 's') { + item = mp_obj_new_bytes(p, cnt); + p += cnt; + res->items[i++] = item; + } else { + while (cnt--) { + item = mp_binary_get_val(fmt_type, *fmt, p_base, &p); + res->items[i++] = item; + } + } + fmt++; + } + return MP_OBJ_FROM_PTR(res); +} + +// This function assumes there is enough room in p to store all the values +STATIC void struct_pack_into_internal(mp_obj_t fmt_in, + byte* p, + size_t n_args, + const mp_obj_t* args) { + const char* fmt = mp_obj_str_get_str(fmt_in); + char fmt_type = get_fmt_type(&fmt); + + byte* p_base = p; + size_t i; + for (i = 0; i < n_args;) { + mp_uint_t cnt = 1; + if (*fmt == '\0') { + // more arguments given than used by format string; CPython raises + // struct.error here + break; + } + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + + if (*fmt == 's') { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ); + mp_uint_t to_copy = cnt; + if (bufinfo.len < to_copy) { + to_copy = bufinfo.len; + } + memcpy(p, bufinfo.buf, to_copy); + memset(p + to_copy, 0, cnt - to_copy); + p += cnt; + } else { + // If we run out of args then we just finish; CPython would raise + // struct.error + while (cnt-- && i < n_args) { + mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p); + } + } + fmt++; + } +} + +STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t* args) { + // TODO: "The arguments must match the values required by the format + // exactly." + mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0])); + vstr_t vstr; + vstr_init_len(&vstr, size); + byte* p = (byte*)vstr.buf; + memset(p, 0, size); + struct_pack_into_internal(args[0], p, n_args - 1, &args[1]); + return mp_obj_new_bytes_from_vstr(&vstr); +} + +STATIC mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t* args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + mp_int_t offset = mp_obj_get_int(args[2]); + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = (mp_int_t)bufinfo.len + offset; + if (offset < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + } + byte* p = (byte*)bufinfo.buf; + byte* end_p = &p[bufinfo.len]; + p += offset; + + // Check that the output buffer is big enough to hold all the values + mp_int_t sz = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0])); + if (p + sz > end_p) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + + struct_pack_into_internal(args[0], p, n_args - 3, &args[3]); + return mp_const_none; +} + +#endif diff --git a/package/struct/_struct.pyi b/package/struct/_struct.pyi new file mode 100644 index 000000000..e69de29bb diff --git a/port/linux/package/pikascript/_struct.pyi b/port/linux/package/pikascript/_struct.pyi new file mode 100644 index 000000000..e69de29bb diff --git a/port/linux/package/pikascript/pikascript-lib/struct/_struct.c b/port/linux/package/pikascript/pikascript-lib/struct/_struct.c new file mode 100644 index 000000000..5554c5f80 --- /dev/null +++ b/port/linux/package/pikascript/pikascript-lib/struct/_struct.c @@ -0,0 +1,255 @@ +/* + * This file is part of the PikaPython project, https://pikapython.com + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2023 Lyon + * + * 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 "_struct.h" +#include "pika_adapter_mpy.h" + +#define _SKIP_COMPILE 1 + +#ifndef _SKIP_COMPILE + +/* + This module implements most of character typecodes from CPython, with + some extensions: + + O - (Pointer to) an arbitrary Python object. This is useful for callback + data, etc. Note that you must keep reference to passed object in + your Python application, otherwise it may be garbage-collected, + and then when you get back this value from callback it may be + invalid (and lead to crash). + S - Pointer to a string (returned as a Python string). Note the + difference from "Ns", - the latter says "in this place of structure + is character data of up to N bytes length", while "S" means + "in this place of a structure is a pointer to zero-terminated + character data". + */ + +STATIC char get_fmt_type(const char** fmt) { + char t = **fmt; + switch (t) { + case '!': + t = '>'; + break; + case '@': + case '=': + case '<': + case '>': + break; + default: + return '@'; + } + // Skip type char + (*fmt)++; + return t; +} + +STATIC mp_uint_t get_fmt_num(const char** p) { + const char* num = *p; + uint len = 1; + while (unichar_isdigit(*++num)) { + len++; + } + char* buff = pika_platform_malloc(len + 1); + pika_platform_memset(buff, 0, len + 1); + pika_platform_memcpy(buff, *p, len); + mp_uint_t val = (mp_uint_t)fast_atoi((char*)num); + *p = num; + return val; +} + +STATIC size_t calc_size_items(const char* fmt, size_t* total_sz) { + char fmt_type = get_fmt_type(&fmt); + size_t total_cnt = 0; + size_t size; + for (size = 0; *fmt; fmt++) { + mp_uint_t cnt = 1; + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + + if (*fmt == 's') { + total_cnt += 1; + size += cnt; + } else { + total_cnt += cnt; + size_t align; + size_t sz = mp_binary_get_size(fmt_type, *fmt, &align); + while (cnt--) { + // Apply alignment + size = (size + align - 1) & ~(align - 1); + size += sz; + } + } + } + *total_sz = size; + return total_cnt; +} + +STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) { + const char* fmt = mp_obj_str_get_str(fmt_in); + size_t size; + calc_size_items(fmt, &size); + return MP_OBJ_NEW_SMALL_INT(size); +} + +STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t* args) { + // unpack requires that the buffer be exactly the right size. + // unpack_from requires that the buffer be "big enough". + // Since we implement unpack and unpack_from using the same function + // we relax the "exact" requirement, and only implement "big enough". + const char* fmt = mp_obj_str_get_str((Arg*)args[0]); + size_t total_sz; + size_t num_items = calc_size_items(fmt, &total_sz); + char fmt_type = get_fmt_type(&fmt); + mp_obj_tuple_t* res = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_items, NULL)); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise((Arg*)args[1], &bufinfo, MP_BUFFER_READ); + byte* p = bufinfo.buf; + byte* end_p = &p[bufinfo.len]; + mp_int_t offset = 0; + + if (n_args > 2) { + // offset arg provided + offset = mp_obj_get_int((Arg*)args[2]); + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = bufinfo.len + offset; + if (offset < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + } + p += offset; + } + byte* p_base = p; + + // Check that the input buffer is big enough to unpack all the values + if (p + total_sz > end_p) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + + for (size_t i = 0; i < num_items;) { + mp_uint_t cnt = 1; + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + mp_obj_t item; + if (*fmt == 's') { + item = mp_obj_new_bytes(p, cnt); + p += cnt; + res->items[i++] = item; + } else { + while (cnt--) { + item = mp_binary_get_val(fmt_type, *fmt, p_base, &p); + res->items[i++] = item; + } + } + fmt++; + } + return MP_OBJ_FROM_PTR(res); +} + +// This function assumes there is enough room in p to store all the values +STATIC void struct_pack_into_internal(mp_obj_t fmt_in, + byte* p, + size_t n_args, + const mp_obj_t* args) { + const char* fmt = mp_obj_str_get_str(fmt_in); + char fmt_type = get_fmt_type(&fmt); + + byte* p_base = p; + size_t i; + for (i = 0; i < n_args;) { + mp_uint_t cnt = 1; + if (*fmt == '\0') { + // more arguments given than used by format string; CPython raises + // struct.error here + break; + } + if (unichar_isdigit(*fmt)) { + cnt = get_fmt_num(&fmt); + } + + if (*fmt == 's') { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ); + mp_uint_t to_copy = cnt; + if (bufinfo.len < to_copy) { + to_copy = bufinfo.len; + } + memcpy(p, bufinfo.buf, to_copy); + memset(p + to_copy, 0, cnt - to_copy); + p += cnt; + } else { + // If we run out of args then we just finish; CPython would raise + // struct.error + while (cnt-- && i < n_args) { + mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p); + } + } + fmt++; + } +} + +STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t* args) { + // TODO: "The arguments must match the values required by the format + // exactly." + mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0])); + vstr_t vstr; + vstr_init_len(&vstr, size); + byte* p = (byte*)vstr.buf; + memset(p, 0, size); + struct_pack_into_internal(args[0], p, n_args - 1, &args[1]); + return mp_obj_new_bytes_from_vstr(&vstr); +} + +STATIC mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t* args) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + mp_int_t offset = mp_obj_get_int(args[2]); + if (offset < 0) { + // negative offsets are relative to the end of the buffer + offset = (mp_int_t)bufinfo.len + offset; + if (offset < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + } + byte* p = (byte*)bufinfo.buf; + byte* end_p = &p[bufinfo.len]; + p += offset; + + // Check that the output buffer is big enough to hold all the values + mp_int_t sz = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0])); + if (p + sz > end_p) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too small")); + } + + struct_pack_into_internal(args[0], p, n_args - 3, &args[3]); + return mp_const_none; +} + +#endif diff --git a/src/PikaObj.h b/src/PikaObj.h index f3a50ffd0..3074e9e5f 100644 --- a/src/PikaObj.h +++ b/src/PikaObj.h @@ -565,12 +565,15 @@ static inline uint8_t obj_refcntNow(PikaObj* self) { #define obj_setStruct(PikaObj_p_self, char_p_name, struct_) \ args_setStruct(((PikaObj_p_self)->list), char_p_name, struct_) -#define PIKA_ERR_STRING_SYNTAX_ERROR "SyntaxError: invalid syntax\r\n" +#define PIKA_ERR_STRING_SYNTAX_ERROR \ + ANSI_COLOR_RED "SyntaxError: invalid syntax\r\n" ANSI_COLOR_RESET #define ABSTRACT_METHOD_NEED_OVERRIDE_ERROR(_) \ obj_setErrorCode(self, 1); \ - pika_platform_printf("Error: abstract method `%s()` need override.\r\n", \ - __FUNCTION__) + pika_platform_printf( \ + ANSI_COLOR_RED \ + "Error: abstract method `%s()` need override.\r\n" ANSI_COLOR_RESET, \ + __FUNCTION__) char* obj_cacheStr(PikaObj* self, char* str); PikaObj* _arg_to_obj(Arg* self, pika_bool* pIsTemp); @@ -613,9 +616,9 @@ const MethodProp floatMethod = { #endif #if PIKA_ARG_CACHE_ENABLE -#define _ARG_HEAP_SIZE_DEF .heap_size = 0, +#define _ARG_HEAP_SIZE_DEF() .heap_size = 0, #else -#define _ARG_HEAP_SIZE_DEF +#define _ARG_HEAP_SIZE_DEF() #endif #if PIKA_KERNAL_DEBUG_ENABLE @@ -634,7 +637,7 @@ const MethodProp floatMethod = { .buffer = (uint8_t*)&_method##Prop \ }, \ .size = sizeof(MethodPropNative), \ - _ARG_HEAP_SIZE_DEF \ + _ARG_HEAP_SIZE_DEF() \ .type = _type, \ .flag = 0, \ .name_hash = _hash, \ @@ -695,14 +698,6 @@ Arg* pika_eventListener_sendSignalAwaitResult(PikaEventListener* self, uint32_t eventId, int eventSignal); -#define COLOR_RED "\x1b[31m" -#define COLOR_GREEN "\x1b[32m" -#define COLOR_YELLOW "\x1b[33m" -#define COLOR_BLUE "\x1b[34m" -#define COLOR_MAGENTA "\x1b[35m" -#define COLOR_CYAN "\x1b[36m" -#define COLOR_RESET "\x1b[0m" - void obj_printModules(PikaObj* self); #if PIKA_DEBUG_ENABLE #define pika_debug(fmt, ...) \ diff --git a/src/PikaPlatform.c b/src/PikaPlatform.c index 51ba8559d..6a379c7ac 100644 --- a/src/PikaPlatform.c +++ b/src/PikaPlatform.c @@ -315,10 +315,7 @@ PIKA_WEAK char pika_platform_getchar(void) { #if defined(__linux) || defined(_WIN32) return getchar(); #else - pika_platform_printf( - "Error: pika_platform_getchar need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(); #endif } @@ -327,9 +324,7 @@ PIKA_WEAK FILE* pika_platform_fopen(const char* filename, const char* modes) { #if defined(__linux) || defined(_WIN32) return fopen(filename, modes); #else - pika_platform_printf("Error: pika_platform_fopen need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(); #endif } @@ -338,10 +333,7 @@ PIKA_WEAK int pika_platform_fclose(FILE* stream) { #if defined(__linux) || defined(_WIN32) return fclose(stream); #else - pika_platform_printf( - "Error: pika_platform_fclose need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(); #endif } @@ -354,10 +346,7 @@ PIKA_WEAK size_t pika_platform_fwrite(const void* ptr, #if defined(__linux) || defined(_WIN32) return fwrite(ptr, size, n, stream); #else - pika_platform_printf( - "Error: pika_platform_fwrite need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(); #endif } @@ -369,9 +358,7 @@ PIKA_WEAK size_t pika_platform_fread(void* ptr, #if defined(__linux) || defined(_WIN32) return fread(ptr, size, n, stream); #else - pika_platform_printf("Error: pika_platform_fread need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(_); #endif } @@ -380,9 +367,7 @@ PIKA_WEAK int pika_platform_fseek(FILE* stream, long offset, int whence) { #if defined(__linux) || defined(_WIN32) return fseek(stream, offset, whence); #else - pika_platform_printf("Error: pika_platform_fseek need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(_); #endif } @@ -391,9 +376,7 @@ PIKA_WEAK long pika_platform_ftell(FILE* stream) { #if defined(__linux) || defined(_WIN32) return ftell(stream); #else - pika_platform_printf("Error: pika_platform_ftell need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(_); #endif } @@ -426,10 +409,7 @@ PIKA_WEAK void pika_platform_sleep_ms(uint32_t ms) { #elif defined(_WIN32) && !defined(CROSS_BUILD) Sleep(ms); #else - pika_platform_printf( - "Error: pika_platform_sleep_ms need implementation!\r\n"); - while (1) { - } + WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(_); #endif } diff --git a/src/PikaPlatform.h b/src/PikaPlatform.h index a187e1673..3d94d3426 100644 --- a/src/PikaPlatform.h +++ b/src/PikaPlatform.h @@ -43,6 +43,14 @@ extern "C" { #include #endif +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_BLUE "\x1b[34m" +#define ANSI_COLOR_MAGENTA "\x1b[35m" +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_RESET "\x1b[0m" + #define PIKA_ASSERT_2(expr, msg, ...) \ if (!(expr)) { \ pika_platform_printf((char*)"Assertion \"%s\" failed, in function: %s(). \r\n (at %s:%d)\n" msg, #expr, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__); \ @@ -309,11 +317,26 @@ void pika_platform_thread_timer_usleep(unsigned long usec); void pika_platform_reboot(void); void pika_platform_clear(void); -#define WEAK_FUNCTION_NEED_OVERRIDE_ERROR(_) \ - pika_platform_printf("Error: weak function `%s()` need override.\r\n", \ - __FUNCTION__); \ +#define WEAK_FUNCTION_NEED_OVERRIDE_INFO(_) \ + pika_platform_printf(ANSI_COLOR_RED \ + "Error: The function `%s()` has been invoked, but " \ + "it is not implemented.\r\n" ANSI_COLOR_RESET, \ + __FUNCTION__); \ + pika_platform_printf( \ + ANSI_COLOR_CYAN \ + "Info: The function `%s()` is defined as a weak function by default. " \ + "It should be overridden on this platform.\r\n" ANSI_COLOR_RESET, \ + __FUNCTION__); + +#define WEAK_FUNCTION_NEED_OVERRIDE_ERROR(_) \ + WEAK_FUNCTION_NEED_OVERRIDE_INFO(_) \ pika_platform_panic_handle(); +#define WEAK_FUNCTION_NEED_OVERRIDE_ERROR_LOWLEVEL(_) \ + WEAK_FUNCTION_NEED_OVERRIDE_INFO(_) \ + while (1) { \ + } + #endif #ifdef __cplusplus diff --git a/src/dataArgs.h b/src/dataArgs.h index f37e6d91d..9928c99b5 100644 --- a/src/dataArgs.h +++ b/src/dataArgs.h @@ -191,6 +191,14 @@ static inline void* pikaDict_getPtr(PikaDict* self, char* name) { return args_getPtr((&((self)->super)), (name)); } +static inline int pikaDict_getSize(PikaDict* self) { + return args_getSize((&((self)->super))); +} + +static inline Arg* pikaDict_getArgByidex(PikaDict* self, int index) { + return args_getArgByIndex((&((self)->super)), (index)); +} + static inline Arg* pikaDict_getArg(PikaDict* self, char* name) { return args_getArg((&((self)->super)), (name)); } diff --git a/src/pika_adapter_mpy.h b/src/pika_adapter_mpy.h index c08ffa0e7..4b197a579 100644 --- a/src/pika_adapter_mpy.h +++ b/src/pika_adapter_mpy.h @@ -1,3 +1,31 @@ +/* + * This file is part of the PikaPython project, http://pikapython.com + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Paul Sokolovsky + * Copyright (c) 2014-2019 Damien P. George + * Copyright (c) 2023 Lyon + * + * 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. + */ + #ifdef __cplusplus extern "C" { #endif @@ -5,6 +33,7 @@ extern "C" { #ifndef __PIKA_ADAPTER_MPY_H__ #define __PIKA_ADAPTER_MPY_H__ #include +#include #include "PikaObj.h" #include "PikaStdData_List.h" #define bool int @@ -15,12 +44,18 @@ extern "C" { /* type define*/ #define STATIC static #define NORETURN +typedef unsigned char byte; +typedef unsigned int uint; +typedef uint32_t unichar; +#define UTF8_IS_NONASCII(ch) ((ch)&0x80) +#define UTF8_IS_CONT(ch) (((ch)&0xC0) == 0x80) /* object API */ #define MP_OBJ_NEW_SMALL_INT(...) arg_newInt(__VA_ARGS__) #define mp_obj_new_bool(...) arg_newInt(__VA_ARGS__) #define mp_obj_new_bytes(...) arg_newBytes(__VA_ARGS__) #define mp_obj_new_float(...) arg_newFloat(__VA_ARGS__) +#define mp_obj_new_int(...) arg_newInt(__VA_ARGS__) #define MP_OBJ_TO_PTR(...) arg_getPtr(__VA_ARGS__) #define MP_OBJ_FROM_PTR(_p) arg_newPtr(ARG_TYPE_OBJECT, (_p)) #define mp_obj_get_int(...) arg_getInt(__VA_ARGS__) @@ -29,6 +64,13 @@ extern "C" { #define mp_const_false arg_newInt(0) #define mp_const_none arg_newNull() +#define mp_obj_new_int_from_ll mp_obj_new_int +#define mp_obj_new_int_from_ull mp_obj_new_int +#define mp_obj_new_float_from_f mp_obj_new_float +#define mp_obj_new_float_from_d mp_obj_new_float + +#define MP_OBJ_SMALL_INT_VALUE(...) arg_getInt(__VA_ARGS__) + /* module API */ #define MP_DEFINE_CONST_DICT(...) #define MP_DEFINE_CONST_FUN_OBJ_KW(...) @@ -94,7 +136,9 @@ static inline int mp_obj_str_get_qstr(Arg* arg) { return hash_time33(arg_getStr(arg)); } -#define mp_obj_new_str(str, len) arg_newStr(str) +static inline Arg* mp_obj_new_str(const char* str, size_t len) { + return arg_newStrN((char*)str, len); +} typedef struct _mp_buffer_info_t { void* buf; // can be NULL if len == 0 @@ -110,14 +154,14 @@ static inline Arg* mp_obj_new_list(int n, Arg** items) { return arg_newObj(list); } -static inline mp_obj_tuple_t* mp_obj_new_tuple(int n, Arg** items_in) { +static inline mp_obj_t mp_obj_new_tuple(int n, Arg** items_in) { mp_obj_tuple_t* tuple = (mp_obj_tuple_t*)malloc(sizeof(mp_obj_tuple_t)); Arg** items = (Arg**)malloc(sizeof(Arg*) * n); if (NULL == items_in) { tuple->len = n; tuple->items = items; } - return tuple; + return arg_newPtr(ARG_TYPE_POINTER, tuple); } static inline void mp_obj_list_append(Arg* list, mp_obj_tuple_t* tuple) { @@ -158,20 +202,20 @@ static inline void pks_load_mp_map(PikaDict* kw, mp_map_t* map) { map->table = (mp_map_elem_t*)malloc(sizeof(mp_map_elem_t) * len); for (int i = 0; i < len; i++) { Arg* item = pikaDict_getArgByidex(kw, i); - map->table[i].key = arg_getNameHash(item); + map->table[i].key = arg_newInt(arg_getNameHash(item)); map->table[i].value = item; } } -static inline void mp_get_buffer_raise(mp_obj_t item, +static inline void mp_get_buffer_raise(const mp_obj_t item, mp_buffer_info_t* buf, char* msg) { - buf->len = arg_getSize(item); + buf->len = arg_getSize((Arg*)item); buf->buf = malloc(buf->len); if (NULL == buf->buf) { mp_raise_OSError(msg); } - memcpy(buf->buf, arg_getBytes(item), buf->len); + memcpy(buf->buf, arg_getBytes((Arg*)item), buf->len); } static const ArgType mp_type_tuple = ARG_TYPE_TUPLE; @@ -218,6 +262,10 @@ typedef struct _mp_rom_map_elem_t { } mp_rom_map_elem_t; #define MICROPY_OBJ_BASE_ALIGNMENT +#define MP_ALIGN(ptr, alignment) \ + (void*)(((uintptr_t)(ptr) + ((alignment)-1)) & ~((alignment)-1)) +#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + typedef struct _mp_obj_type_t mp_obj_type_t; struct _mp_obj_base_t { const mp_obj_type_t* type MICROPY_OBJ_BASE_ALIGNMENT; @@ -337,6 +385,366 @@ typedef struct _mp_obj_list_t { mp_obj_t* items; } mp_obj_list_t; +// attribute flags +#define FL_PRINT (0x01) +#define FL_SPACE (0x02) +#define FL_DIGIT (0x04) +#define FL_ALPHA (0x08) +#define FL_UPPER (0x10) +#define FL_LOWER (0x20) +#define FL_XDIGIT (0x40) + +// shorthand character attributes +#define AT_PR (FL_PRINT) +#define AT_SP (FL_SPACE | FL_PRINT) +#define AT_DI (FL_DIGIT | FL_PRINT | FL_XDIGIT) +#define AT_AL (FL_ALPHA | FL_PRINT) +#define AT_UP (FL_UPPER | FL_ALPHA | FL_PRINT) +#define AT_LO (FL_LOWER | FL_ALPHA | FL_PRINT) +#define AT_UX (FL_UPPER | FL_ALPHA | FL_PRINT | FL_XDIGIT) +#define AT_LX (FL_LOWER | FL_ALPHA | FL_PRINT | FL_XDIGIT) + +// table of attributes for ascii characters +STATIC const uint8_t attr[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, AT_SP, AT_SP, + AT_SP, AT_SP, AT_SP, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AT_SP, + AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, + AT_PR, AT_PR, AT_PR, AT_PR, AT_DI, AT_DI, AT_DI, AT_DI, AT_DI, AT_DI, AT_DI, + AT_DI, AT_DI, AT_DI, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_UX, + AT_UX, AT_UX, AT_UX, AT_UX, AT_UX, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, + AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, AT_UP, + AT_UP, AT_UP, AT_UP, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_PR, AT_LX, AT_LX, + AT_LX, AT_LX, AT_LX, AT_LX, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, + AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, AT_LO, + AT_LO, AT_LO, AT_PR, AT_PR, AT_PR, AT_PR, 0}; + +static unichar utf8_get_char(const byte* s) { + unichar ord = *s++; + if (!UTF8_IS_NONASCII(ord)) { + return ord; + } + ord &= 0x7F; + for (unichar mask = 0x40; ord & mask; mask >>= 1) { + ord &= ~mask; + } + while (UTF8_IS_CONT(*s)) { + ord = (ord << 6) | (*s++ & 0x3F); + } + return ord; +} + +static const byte* utf8_next_char(const byte* s) { + ++s; + while (UTF8_IS_CONT(*s)) { + ++s; + } + return s; +} + +static mp_uint_t utf8_ptr_to_index(const byte* s, const byte* ptr) { + mp_uint_t i = 0; + while (ptr > s) { + if (!UTF8_IS_CONT(*--ptr)) { + i++; + } + } + + return i; +} + +static size_t utf8_charlen(const byte* str, size_t len) { + size_t charlen = 0; + for (const byte* top = str + len; str < top; ++str) { + if (!UTF8_IS_CONT(*str)) { + ++charlen; + } + } + return charlen; +} + +// Be aware: These unichar_is* functions are actually ASCII-only! +static bool unichar_isspace(unichar c) { + return c < 128 && (attr[c] & FL_SPACE) != 0; +} + +static bool unichar_isalpha(unichar c) { + return c < 128 && (attr[c] & FL_ALPHA) != 0; +} + +/* unused +bool unichar_isprint(unichar c) { + return c < 128 && (attr[c] & FL_PRINT) != 0; +} +*/ + +static bool unichar_isdigit(unichar c) { + return c < 128 && (attr[c] & FL_DIGIT) != 0; +} + +static bool unichar_isxdigit(unichar c) { + return c < 128 && (attr[c] & FL_XDIGIT) != 0; +} + +static bool unichar_isident(unichar c) { + return c < 128 && ((attr[c] & (FL_ALPHA | FL_DIGIT)) != 0 || c == '_'); +} + +static bool unichar_isalnum(unichar c) { + return c < 128 && ((attr[c] & (FL_ALPHA | FL_DIGIT)) != 0); +} + +static bool unichar_isupper(unichar c) { + return c < 128 && (attr[c] & FL_UPPER) != 0; +} + +static bool unichar_islower(unichar c) { + return c < 128 && (attr[c] & FL_LOWER) != 0; +} + +static unichar unichar_tolower(unichar c) { + if (unichar_isupper(c)) { + return c + 0x20; + } + return c; +} + +static unichar unichar_toupper(unichar c) { + if (unichar_islower(c)) { + return c - 0x20; + } + return c; +} + +static mp_uint_t unichar_xdigit_value(unichar c) { + // c is assumed to be hex digit + mp_uint_t n = c - '0'; + if (n > 9) { + n &= ~('a' - 'A'); + n -= ('A' - ('9' + 1)); + } + return n; +} + +static bool utf8_check(const byte* p, size_t len) { + uint8_t need = 0; + const byte* end = p + len; + for (; p < end; p++) { + byte c = *p; + if (need) { + if (UTF8_IS_CONT(c)) { + need--; + } else { + // mismatch + return 0; + } + } else { + if (c >= 0xc0) { + if (c >= 0xf8) { + // mismatch + return 0; + } + need = (0xe5 >> ((c >> 3) & 0x6)) & 3; + } else if (c >= 0x80) { + // mismatch + return 0; + } + } + } + return need == 0; // no pending fragments allowed +} + +#ifndef alignof +#define alignof(type) \ + offsetof( \ + struct { \ + char c; \ + type t; \ + }, \ + t) +#endif + +#define BYTEARRAY_TYPECODE 1 + +static size_t mp_binary_get_size(char struct_type, + char val_type, + size_t* palign) { + size_t size = 0; + int align = 1; + switch (struct_type) { + case '<': + case '>': + switch (val_type) { + case 'b': + case 'B': + size = 1; + break; + case 'h': + case 'H': + size = 2; + break; + case 'i': + case 'I': + size = 4; + break; + case 'l': + case 'L': + size = 4; + break; + case 'q': + case 'Q': + size = 8; + break; + case 'P': + case 'O': + case 'S': + size = sizeof(void*); + break; + case 'f': + size = sizeof(float); + break; + case 'd': + size = sizeof(double); + break; + } + break; + case '@': { + // TODO: + // The simplest heuristic for alignment is to align by value + // size, but that doesn't work for "bigger than int" types, + // for example, long long may very well have long alignment + // So, we introduce separate alignment handling, but having + // formal support for that is different from actually supporting + // particular (or any) ABI. + switch (val_type) { + case BYTEARRAY_TYPECODE: + case 'b': + case 'B': + align = size = 1; + break; + case 'h': + case 'H': + align = alignof(short); + size = sizeof(short); + break; + case 'i': + case 'I': + align = alignof(int); + size = sizeof(int); + break; + case 'l': + case 'L': + align = alignof(long); + size = sizeof(long); + break; + case 'q': + case 'Q': + align = alignof(long long); + size = sizeof(long long); + break; + case 'P': + case 'O': + case 'S': + align = alignof(void*); + size = sizeof(void*); + break; + case 'f': + align = alignof(float); + size = sizeof(float); + break; + case 'd': + align = alignof(double); + size = sizeof(double); + break; + } + } + } + + if (size == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("bad typecode")); + } + + if (palign != NULL) { + *palign = align; + } + return size; +} + +// The long long type is guaranteed to hold at least 64 bits, and size is at +// most 8 (for q and Q), so we will always be able to parse the given data +// and fit it into a long long. +static inline long long mp_binary_get_int(size_t size, + bool is_signed, + bool big_endian, + const byte* src) { + int delta; + if (!big_endian) { + delta = -1; + src += size - 1; + } else { + delta = 1; + } + + unsigned long long val = 0; + if (is_signed && *src & 0x80) { + val = -1; + } + for (uint i = 0; i < size; i++) { + val <<= 8; + val |= *src; + src += delta; + } + + return val; +} + +#define is_signed(typecode) (typecode > 'Z') +static mp_obj_t mp_binary_get_val(char struct_type, + char val_type, + byte* p_base, + byte** ptr) { + byte* p = *ptr; + size_t align; + + size_t size = mp_binary_get_size(struct_type, val_type, &align); + if (struct_type == '@') { + // Align p relative to p_base + p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align); +#if MP_ENDIANNESS_LITTLE + struct_type = '<'; +#else + struct_type = '>'; +#endif + } + *ptr = p + size; + + long long val = + mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p); + + if (val_type == 'O') { + return (mp_obj_t)(mp_uint_t)val; + } else if (val_type == 'S') { + const char* s_val = (const char*)(uintptr_t)(mp_uint_t)val; + return mp_obj_new_str(s_val, strlen(s_val)); + } else if (val_type == 'f') { + union { + uint32_t i; + float f; + } fpu = {val}; + return mp_obj_new_float_from_f(fpu.f); + } else if (val_type == 'd') { + union { + uint64_t i; + double f; + } fpu = {val}; + return mp_obj_new_float_from_d(fpu.f); + } else if (is_signed(val_type)) { + return mp_obj_new_int_from_ll(val); + } else { + return mp_obj_new_int_from_ull(val); + } +} + #endif #ifdef __cplusplus }