MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.
microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
python编程示例系列 python编程示例系列二
python的Web神器Streamlit
这段代码定义了 MicroPython 中流的通用操作方法,包括读写、定位、刷新等。代码中使用了 MicroPython 的内部对象和方法,如 mp_obj_t
类型、mp_stream_p_t
流协议等。此外,还提供了类似 POSIX 的接口,以便将现有的 POSIX 兼容软件移植到 MicroPython 流上。代码中的注释详细说明了每个函数的功能和用法。
#include <string.h>
#include <unistd.h>
#include "py/objstr.h"
#include "py/stream.h"
#include "py/runtime.h"
// 此文件定义了通用的 Python 流读写方法,这些方法分派到对象的底层流接口。
// TODO: 应该在 mpconfig.h 中定义
#define DEFAULT_BUFFER_SIZE 256
// 读取流中的所有数据
static mp_obj_t stream_readall(mp_obj_t self_in);
// 返回 *errcode 非零时的错误条件,如果有,返回发生错误之前的字节数。
// 如果 *errcode == 0,返回总字节数写入。
mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode, byte flags) {
byte *buf = buf_;
// 定义读写函数类型
typedef mp_uint_t (*io_func_t)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
io_func_t io_func;
const mp_stream_p_t *stream_p = mp_get_stream(stream);
// 根据标志确定是读取还是写入操作
if (flags & MP_STREAM_RW_WRITE) {
io_func = (io_func_t)stream_p->write;
} else {
io_func = stream_p->read;
}
*errcode = 0;
mp_uint_t done = 0;
while (size > 0) {
mp_uint_t out_sz = io_func(stream, buf, size, errcode);
// 对于读取,out_sz == 0 表示 EOF。对于写入,它的具体含义是不确定的,
// 但我们没有取得任何进展,所以返回仍然是最佳选择。
if (out_sz == 0) {
return done;
}
if (out_sz == MP_STREAM_ERROR) {
// 如果我们在得到 EAGAIN 之前读到了一些数据,不要泄露它
if (mp_is_nonblocking_error(*errcode) && done != 0) {
*errcode = 0;
}
return done;
}
if (flags & MP_STREAM_RW_ONCE) {
return out_sz;
}
buf += out_sz;
size -= out_sz;
done += out_sz;
}
return done;
}
// 根据 offset 和 whence 参数进行流定位
mp_off_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) {
struct mp_stream_seek_t seek_s;
seek_s.offset = offset;
seek_s.whence = whence;
const mp_stream_p_t *stream_p = mp_get_stream(stream);
mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, errcode);
if (res == MP_STREAM_ERROR) {
return (mp_off_t)-1;
}
return seek_s.offset;
}
// 获取流对象并检查是否支持指定的操作,如果不支持则抛出异常
const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) {
const mp_obj_type_t *type = mp_obj_get_type(self_in);
if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) {
const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(type, protocol);
// 检查流是否支持读取、写入或ioctl操作
if (!((flags & MP_STREAM_OP_READ) && stream_p->read == NULL)
&& !((flags & MP_STREAM_OP_WRITE) && stream_p->write == NULL)
&& !((flags & MP_STREAM_OP_IOCTL) && stream_p->ioctl == NULL)) {
return stream_p;
}
}
// 如果流不支持操作,则抛出异常
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported"));
}
// 通用的流读取函数,支持读取指定数量的字节或读取所有数据
static mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) {
// 如果未指定读取大小或大小为 -1,则读取所有数据
mp_int_t sz;
if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) {
return stream_readall(args[0]);
}
const mp_stream_p_t *stream_p = mp_get_stream(args[0]);
// 如果流是文本模式,则需要读取指定数量的 Unicode 字符
#if MICROPY_PY_BUILTINS_STR_UNICODE
if (stream_p->is_text) {
// 由于我们没有缓冲,并且流 API 只能读取字节,我们必须按字节读取
// 并且不能过度读取。如果我们想要 sz 个字符,那么读取 sz 字节永远不会过度读取,
// 因此我们遵循这种方法,在循环中继续读取,直到我们有足够数量的字符。
// 这对于只有 ASCII 字符的文本将是 1 次读取,对于有几个非 ASCII 字符的文本
// 将是大约 2 次读取。对于有很多非 ASCII 字符的文本,这将是非常低效的
// 在时间和内存上。
vstr_t vstr;
vstr_init(&vstr, sz);
mp_uint_t more_bytes = sz;
mp_uint_t last_buf_offset = 0;
while (more_bytes > 0) {
char *p = vstr_add_len(&vstr, more_bytes);
int error;
mp_uint_t out_sz = mp_stream_read_exactly(args[0], p, more_bytes, &error);
if (error != 0) {
vstr_cut_tail_bytes(&vstr, more_bytes);
if (mp_is_nonblocking_error(error)) {
// 对于非阻塞流,我们尽可能多地读取。
// 如果我们什么也没读到,就像 read() 一样返回 None。
// 否则,返回到目前为止读取的数据。
// TODO 如果我们只读取了半个非 ASCII 字符怎么办?
if (vstr.len == 0) {
vstr_clear(&vstr);
return mp_const_none;
}
break;
}
mp_raise_OSError(error);
}
if (out_sz < more_bytes) {
// 完成读取。
// TODO 如果我们只读取了半个非 ASCII 字符怎么办?
vstr_cut_tail_bytes(&vstr, more_bytes - out_sz);
if (out_sz == 0) {
break;
}
}
// 从刚刚读取的字节中计算字符数
for (mp_uint_t off = last_buf_offset;;) {
byte b = vstr.buf[off];
int n;
if (!UTF8_IS_NONASCII(b)) {
// 1 字节的 ASCII 字符
n = 1;
} else if ((b & 0xe0) == 0xc0) {
// 2 字节的字符
n = 2;
} else if (